Simpler/cleaner 在 VBA 中使用大量命名单元格时指定范围的方法?

Simpler/cleaner way to specify ranges when using lots of named cells in VBA?

在我正在开发的一个相当大的 Excel 程序中,我在电子表格中有很多命名单元格,这些单元格填充了数据,以便在用户决定移动时帮助保持格式一致周围的模板设置,并避免在各种潜艇中硬编码 row/column 数字。不幸的是,这导致我的代码中有很多丑陋的范围引用,如下例所示。是否有一种 simpler/cleaner 看起来更易读的方式来编写我缺少的这些内容?

'Examples of ugly range references:
Range(Range("GS_BeginData").Offset((counter + m), -1), Range("GS_BeginData").Offset((counter + m), 2))

Range(Range("GS_BeginData").Offset(counter, 1), Range("GS_BeginData").Offset((counter + fileCount), 1))

Range("SS_Unit", Range("SS_Unit").Offset(0, 1))

Range("SS_BeginData", Range("SS_BeginData").End(xlDown))

Cells((Range("SS_BeginData").Row + i), (Range("SS_BeginData").Column + 1))

我想我总是可以在 Subs 的开头为这些不同的命名单元格和范围设置简单的命名变量,以清理代码的外观,但我有很多,我真的不想在我所有的 Subs 的开头添加大量的变量声明行。

更容易阅读的方式 -- 是的。根据需要声明和分配对象变量。至少,您可以定义 "GS_BeginData" 和 "SS_Unit" 以及 "SS_BeginData" 的范围,这将使您的代码更具可读性和可维护性。

这样,如果您更改了命名范围引用的 names,您只需要更新分配给初始对象的几行代码,而不是每个字面引用,例如 "GS_BeginData",等等

Dim GSBegin as Range, SSBegin as Range, SSUnit as Range

Set GSBegin = Range("GS_BeginData")
Set SSBegin = Range("SS_BeginData")
Set SSUnit = Range("SS_Unit")

那么,你丑陋的引用就可以修改了:

Range(GSBegin.Offset((counter + m), -1), GSBegin.Offset((counter + m), 2))

Range(GSBegin.Offset(counter, 1), GSBegin.Offset((counter + fileCount), 1))

SSUnit.Offset(0, 1)

Range(SSBegin, SSBegin.End(xlDown))

Cells((SSBegin.Row + i), (SSBegin.Column + 1))
  1. 使用变量! (Dim 的代价很小)
  2. 利用其他 Range 属性。在这些情况下 Resize 很有用

.

Dim GS_BeginData As Range
Set GS_BeginData = Range("GS_BeginData")
'Set r = Range(Range("GS_BeginData").Offset((counter + m), -1), Range("GS_BeginData").Offset((counter + m), 2))
Set r = GS_BeginData.Offset(counter + m, -1).Resize(, 3)

'Set r = Range(Range("GS_BeginData").Offset(counter, 1), Range("GS_BeginData").Offset((counter + fileCount), 1))
Set r = GS_BeginData.Offset(counter, 1).Resize(fileCount, 1)

Dim SS_Unit As Range
Set SS_Unit = Range("SS_Unit")
'Set r = Range("SS_Unit", Range("SS_Unit").Offset(0, 1))
Set r = SS_Unit.Resize(, 2)
Set r = SS_Unit.Resize(, SS_Unit.Columns.Count + 1) ' if SS_Unit has more than 1 column

Dim SS_BeginData As Range
Set SS_BeginData = Range("SS_BeginData")
'Set r = Range("SS_BeginData", Range("SS_BeginData").End(xlDown))
Set r = Range(SS_BeginData, SS_BeginData.End(xlDown))

'Set r = Cells((Range("SS_BeginData").Row + i), (Range("SS_BeginData").Column + 1))
Set r = SS_BeginData.Offset(i, 1).Resize(1, 1)

我不使用命名范围,因为它们难以维护并且很快就会变得一团糟。我总是使用这两个功能:

Function RowNumber(Header As String, _
             Optional Sh As Worksheet, _
             Optional FromColumn As Integer = 1, _
             Optional IgnoreError As Boolean = False) As Integer
  If Sh Is Nothing Then Set Sh = ActiveSheet

  Dim R As Integer
  For R = 1 To Sh.UsedRange.Row + Sh.UsedRange.Rows.Count - 1
    If Sh.Cells(R, FromColumn) = Header Then RowNumber = R: Exit Function
  Next R

  If Not IgnoreError Then MsgBox "Row """ & Header & """ not found", vbCritical
End Function

Function ColumnNumber(Header As String, _
             Optional Sh As Worksheet, _
             Optional FromRow As Integer = 1, _
             Optional IgnoreError As Boolean = False) As Integer
  If Sh Is Nothing Then Set Sh = ActiveSheet

  Dim C As Integer
  For C = 1 To Sh.UsedRange.Column + Sh.UsedRange.Columns.Count - 1
    If Sh.Cells(FromRow, C) = Header Then ColumnNumber = C: Exit Function
  Next C

  If Not IgnoreError Then MsgBox "Column """ & Header & """ not found", vbCritical
End Function

然后我得到类似这样的值:

Value = Sh.Cells(RowNumber("Header", Sh), 2)

我有时会使用另一个版本 memoization

如果你真的在许多不同的 Subs 中使用它,你也可以创建一个函数来 DRY 你的代码,比如。

Function GSBeginOffsetRange(a As Long, b As Long, c As Long, d As Long) As Range
    Dim GSBegin As Range
    Set GSBegin = Range("GS_BeginData")
    Set GSBeginOffsetRange = Range(GSBegin.Offset(a, b), GSBegin.Offset(c, d))
End Function

这将使您的第一行示例:

GSBeginOffsetRange((counter + m), -1, (counter + m), 2)

您能否简单地列出所有命名范围,然后遍历该列表中的每个项目?

http://www.mrexcel.com/forum/excel-questions/44907-list-out-named-ranges-visual-basic-applications.html