我有一个跨越行和列的不连续选择,并且我想对其进行For Each循环。 Excel VBA通过首先向下循环第1列,然后向下循环2,3等来做到这一点;但我希望它先沿行循环。
(我的工作表看起来像下面的图片,我需要依次循环选择(版本)每列,并检索文档号和其他信息。工作表中的行数和版本列数为不固定)。
写一个相当大的Sort函数并创建一个引用数组的时间很短,我想知道是否有一种“内置”方式来做到这一点?
我不需要代码,只需一个解释。
答案 0 :(得分:2)
For Each
迭代对象集合的顺序与实现有关(IOW责备Excel,而不是VBA),并且尽管可能是确定的和可预测的,但其规范中没有任何内容可以保证特定的迭代顺序。因此,为迭代对象集合而编写的VBA代码不应在特定的迭代顺序的假设下编写,因为在所涉及的类型库的各个版本(此处为Excel)之间可以很好地进行更改。
尚不清楚Range
/ Selection
的形状是什么,但是如果您需要以特定顺序迭代选定的单元格,则不应使用For Each
循环,至少不是为了迭代细胞本身。
由于范围不连续,因此Range
将具有多个Areas
;您需要迭代Selection.Areas
,然后为每个选定区域 进行特定顺序的迭代。到目前为止,For Each
是Range.Areas
的{{3}}。
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
'todo
Next
创建一个以currentArea
作为参数的单独过程,而不是most efficient way to iterate an object collection-在该过程中,您将迭代各个单元格:
Private Sub ProcessContiguousArea(ByVal area As Range)
Dim currentRow As Long
For currentRow = 1 To area.Rows.Count
Debug.Print area.Cells(currentRow, 1).Address
Next
End Sub
现在,外循环看起来像这样:
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
ProcessContiguousArea currentArea
Next
ProcessContiguousArea
过程可以自由地执行给定连续区域所需的任何操作,只需使用For
循环逐行迭代范围,而无需关心实际的地址。选定区域:使用Range.Cells(RowIndex, ColumnIndex)
,第1行/第1列代表该范围的左上角单元,无论该范围在工作表中位于何处。
可以使用Range.Offset
访问未选择的单元格:
Debug.Print area.Cells(currentRow, 1).Offset(ColumnOffset:=10).Address
area
返回工作表上area.Row
左上角单元格的行,而工作表上area
的左上角单元格列则用{{ 1}}。
答案 1 :(得分:1)
通过首先遍历行( i ),您将获得“ 按行顺序”,例如A1,B1,C1,...
Sub NonContiguous()
Dim i As Long
Dim j As Long
Dim k As Long
With Selection
For k = 1 To .Areas.Count
With .Areas(k)
For i = .Row To .Rows.Count + .Row - 1
For j = .Column To .Columns.Count + .Column - 1
Debug.Print .Parent.Cells(i, j).Address & " = " _
& .Parent.Cells(i, j)
Next
Next
End With
Next
End With
End Sub
答案 2 :(得分:0)
这是基于urdearboy的建议:
1.遍历列
2.在列中,遍历单元格
Sub disjoint()
Dim r As Range, rInt As Range
Dim nLastColumn As Long
Dim nFirstColumn As Long, msg As String
Dim N As Long
Set r = Range("C3,C9,E6,E13,E15,G1,G2,G3,G4")
nFirstColumn = Columns.Count
nLastColumn = 0
msg = ""
For Each rr In r
N = rr.Column
If N < nFirstColumn Then nFirstColumn = N
If N > nLastColumn Then nLastColumn = N
Next rr
For N = nFirstColumn To nLastColumn
Set rInt = Intersect(Columns(N), r)
If rInt Is Nothing Then
Else
For Each rr In rInt
msg = msg & vbCrLf & rr.Address(0, 0)
Next rr
End If
Next N
MsgBox msg
End Sub