我试图弄清楚如何在大范围内的特定行上工作。但是,使用rows属性创建的范围似乎与简单范围的行为不同。检查以下代码,第一次定义变量SpecificRow时,无法选择特定的单元格。然而,通过重新定义范围的奇怪解决方法,它工作正常。您是否知道为什么以及如何以更优雅的方式定义范围?
'The following shows the weird behavior of Rows property
Dim SourceRng As Range
Dim SpecificRow As Range
Dim i As Long
i = 3
Set SourceRng = Range("A1:D20")
Set SpecificRow = SourceRng.Rows(i)
'This will show the address of the selected row ("A3:D3")
MsgBox SpecificRow.Address
'Unexplicable behavior of the range when trying to select a specific cell
'where it will instead consider the whole row (within the limits of SourceRng)
MsgBox SpecificRow(1).Address
MsgBox SpecificRow(2).Address
'This would send an error
'MsgBox SpecificRow(1, 1).Address
'Workaround
Set SpecificRow = Intersect(SpecificRow, SpecificRow)
'The following will select the same address than before
MsgBox SpecificRow.Address
'However, now it has a correct behavior when selecting a specific cell
MsgBox SpecificRow(1).Address
MsgBox SpecificRow(2).Address
答案 0 :(得分:5)
如果您将索引属性传递给错误的参数,则应该会出现奇怪的行为。正如您的代码所示,SourceRng.Rows(i)
返回的范围实际上是正确的。它只是没有做你认为它做的事情。 Rows
的{{1}}属性只返回指向它所调用的完全相同的Range
对象的指针。您可以在其类型库定义中看到:
Range
请注意,它不会采取任何参数。返回的HRESULT _stdcall Rows([out, retval] Range** RHS);
对象就是您为其提供索引的对象,您可以根据其Range
的默认属性对其进行索引(从技术上讲,它是' s) Item
,但2可互换)。第一个参数(您唯一使用_Default
传递的参数)是Rows(i)
。因此RowIndex
与Rows(i)
完全相同。您实际上可以在您提供Rows.Item(RowIndex:=i)
索引时弹出的IntelliSense工具提示中 参见 :
Excel会在此调用中以不同方式处理索引,因为为第二个参数提供任何值参数是运行时错误' 1004'。请注意,当您致电Row
时,会进行类似的财产通话。同样,SpecificRow(1).Address
的默认属性为Range
,因此您需要再次指定一行 - 不一列。 Range.Item()
与SpecificRow(1).Address
完全相同。
Excel中的奇怪之处似乎是SpecificRow.Item(RowIndex:=1).Address
"忘记"返回的Range
。事实是它是在Range.Rows
调用的上下文中调用的,并且不再抑制列索引器。请记住,从上面的typelib定义中返回的对象只是指向原始Rows
对象的指针 back 。这意味着Range
"泄漏"在狭隘的背景下。
考虑到所有事情,我说Excel SpecificRow(2)
实现有点像黑客。 Rows
显然正在给你一个新的" hard"范围对象,但最后两行代码是 不 您应该考虑的内容"更正"行为。同样,当您仅向Application.Intersect(SpecificRow, SpecificRow)
提供第一个参数时,它会被声明为Range.Items
:
出现会发生什么是Excel确定此时RowIndex
中只有一行,并假设传递的单个参数是Range
。
正如@CallumDA所指出的那样,你可以完全依赖默认属性并明确提供你需要的所有索引,即:
,从而避免所有这些松鼠行为。ColumnIndex
答案 1 :(得分:2)
这就是我如何处理这些行中的行和特定单元格。唯一真正的区别是使用Sub WorkingWithRows()
Dim rng As Range, rngRow As Range
Set rng = Sheet1.Range("A1:C3")
For Each rngRow In rng.Rows
Debug.Print rngRow.Cells(1, 1).Address
Debug.Print rngRow.Cells(1, 2).Address
Debug.Print rngRow.Cells(1, 3).Address
Next rngRow
End Sub
:
$A$1
$B$1
$C$1
$A$2
$B$2
$C$2
$A$3
$B$3
$C$3
返回:
{{1}}
正如您所料
答案 2 :(得分:1)
我找不到任何适当的文档,但这种观察到的行为实际上看起来非常符合逻辑。
Excel中的Range
类有两个重要属性:
Range
的单个实例足以表示工作表上的任何可能范围For Each
循环中使用) 我相信为了实现逻辑上的可迭代性而又避免创建不必要的实体(即CellsCollection
,RowsCollection
和ColumnsCollection
等单独的类, Excel开发人员想出了一个设计,其中Range
的每个实例都拥有一个private
属性,告诉它它将自己计算哪些单位(这样一个范围可以是“行的集合”)另一个范围可能是“一组细胞”。)
当您通过"rows"
属性创建范围时,此属性设置为(例如)Rows
,当您通过{{1}创建范围时(例如)"columns"
当你以任何其他方式创建一个范围时,属性和(比如)Columns
。
这使您可以做到这一点而不会感到不必要的惊讶:
"cells"
For Each r In SomeRange.Rows
' will iterate through rows
Next
这里For Each c In SomeRange.Columns
' will iterate through columns
Next
和Rows
都返回相同的类型Columns
,它指的是完全相同的工作表区域,但Range
循环通过行中的行进行迭代第一个案例和第二个案例中的列,好像For Each
和Rows
返回了两种不同的类型(Columns
和RowsCollection
)。
有意义的是它是以这种方式设计的,因为ColumnsCollection
循环的重要属性是它不能为For Each
对象提供多个参数以获取下一个项目(单元格,行或列)。事实上,Range
根本无法提供任何参数,它只能询问“下一个请”。
为了支持这一点,For Each
类必须能够提供下一个没有参数的“东西”,即使范围是二维的并且需要两个坐标来获取“某些东西”。这就是为什么Range
的每个实例必须记住它将以什么单位计算自己。
该设计的一个副作用是,在仅提供一个坐标的Range
中查找“某事”是完全正确的。这正是Range
机制的作用,我们只是直接跳转到For Each
项。
当迭代(或索引到)i
返回的范围时,我们将从上到下获得Rows
行;对于i
返回的范围,我们从左到右得到Columns
列;对于i
或任何其他方法返回的范围,我们将获得Cells
个单元格,从左上角到右上角再到底部。
这种设计的另一个副作用是可以以有意义的方式“走出”范围。也就是说,如果你有一个三个单元格的范围,并且你要求第四个单元格,你仍然可以得到它,它将是由范围的形状和它自己计算的单位决定的单元格:
i
因此,Dim r As Range
Set r = Range("A1:C3") ' Contains 9 cells
Debug.Print r.Cells(12).Address ' $C$4 - goes outside of the range but maintains its shape
的解决方法会将特定Set SpecificRow = Intersect(SpecificRow, SpecificRow)
实例的内部计数模式从(例如)Range
重置为(比方说)"rows"
。
你可以用
实现同样的目标"cells"
但最好让Set SpecificRow = SpecificRow.Cells
MsgBox SpecificRow(1).Address
接近使用点而不是范围创建点:
Cells