我正在尝试使用我在For Each Function, to loop through specifically named worksheets找到的一些代码来遍历工作簿中的指定工作表,运行少量代码并转到下一页。
Sub LoopThroughSheets()
Dim Assets As Worksheet
Dim Asset As Worksheet
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For Each Asset In Assets
'my code here
MsgBox ActiveSheet.Name 'test loop
Next Asset
End Sub
这不是在表格中循环。我试过了Dim Assets as Worksheet
,但这打破了代码。
任何帮助非常感谢,
干杯
答案 0 :(得分:13)
您在问题中显示的代码因以下原因而失败:
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
Assets
是一种工作表,它是一种Object,在为对象赋值时必须使用Set
:
Set Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
这会失败,因为Array("…")
不是工作表。
您暗示您的代码的早期版本会运行,但不会遍历工作表。原因是:
MsgBox ActiveSheet.Name
这将显示活动工作表的名称,但此循环中的任何内容都不会更改活动工作表。
我对你的解决方案不满意,尽管它没有明显的错误。我看到太多的程序失败了,因为程序员在一个语句中做了太多。首先,声明越复杂,首先要做的就越久,在后续维护中理解的时间就越长。有时原来的程序员得到的陈述略有错误;有时维护程序员在尝试更新它时会出错。在每种情况下,程序员花费的额外时间都没有证明运行时的任何节省。
Alex K已根据VBA的要求重新定义Assets
和Asset
作为变体,并添加Sheets(Asset).Select
以更改哪个工作表处于活动状态,从而修复了您的代码。我不能赞同这一点,因为Select
是一个缓慢的陈述。特别是,如果您不包含Application.ScreenUpdating = False
,那么当您从每个Select
重新绘制屏幕时,您的例程的持续时间可以通过屋顶。
在解释我的解决方案之前,有一些关于变体的背景知识。
如果我写:
Dim I as Long
I
将始终为长整数。
在运行时,编译器/解释器不必考虑遇到I
时的内容:
I = I + 5
但是假设我写道:
Dim V as Variant
V = 5
V = V + 5
V = "Test"
V = V & " 1"
这是完全有效(有效但不明智)的代码,因为Variant可以包含数字,字符串或工作表。但是每当我的代码访问V时,解释器必须检查V的当前内容的类型,并确定它是否适合当前情况。这很费时间。
我不想阻止你在适当的时候使用Variants,因为它们非常有用,但你需要知道它们的开销。
接下来我希望提倡使用有意义和系统的名称。我根据我多年来使用的系统命名我的变量。我可以查看我的任何程序/宏,并知道变量是什么。当我需要更新12或15个月前编写的程序/宏时,这可以节省时间。
我不喜欢:
Dim Assets As Variant
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
因为" pipe_mat_tables"等不是资产;它们是工作表的名称。我会写:
Dim WshtNames As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
我的第一个提议是:
Option Explicit
Sub Test1()
Dim WshtNames As Variant
Dim WshtNameCrnt As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For Each WshtNameCrnt In WshtNames
With Worksheets(WshtNameCrnt)
Debug.Print "Cell B1 of worksheet " & .Name & " contains " & .Range("B1").Value
End With
Next WshtNameCrnt
End Sub
我可以将WshtNameCrnt
命名为WshtName
,但我被告知名称应至少相差三个字符以避免使用错误的字符而不注意。
Array
函数返回包含数组的变体。 For Each
语句的控制变量必须是对象或变体。这就是我将WshtNames
和WshtNameCrnt
定义为变体的原因。请注意,您的解决方案有效,因为工作表是一个对象。
我使用了With Worksheets(WshtNameCrnt)
,这意味着匹配的End With
之前的任何代码都可以通过在开头有一个句点来访问此工作表的组件。因此.Name
和.Range("B1").Value
引用Worksheets(WshtNameCrnt)
而不选择工作表。这比任何替代方案都更快更清晰。
我使用Debug.Print
而不是MsgBox
,因为它不那么麻烦了。我的代码运行时没有必须为每个工作表按 Return ,我在立即窗口中有一个整洁的列表,我可以在闲暇时查看。在开发期间,我的代码中经常有很多Debug.Print
语句,这就是为什么我输出一个句子而不仅仅是工作表名称或单元格值。
我的第二个提议是:
Sub Test2()
Dim InxW As Long
Dim WshtNames As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For InxW = LBound(WshtNames) To UBound(WshtNames)
With Worksheets(WshtNames(InxW))
Debug.Print "Cell B1 of worksheet " & .Name & " contains " & .Range("B1").Value
End With
Next InxW
End Sub
此宏与第一个宏具有相同的效果。我有时会发现For
比For Each
更方便,尽管在这种情况下我无论如何都看不到任何优势。请注意,即使WshtNames的下限始终为零,我也写了LBound(WshtNames)
。这只是我(过度?过度)精确。
希望这有帮助。
答案 1 :(得分:4)
解决了它,但总是很高兴听到其他方法
Sub loopsheets()
Dim Sh As Worksheet
For Each Sh In Sheets(Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables"))
MsgBox Sh.Range("b1")
Next
End Sub
干杯
答案 2 :(得分:-2)
使用变体而不是工作表。
Array
返回一个字符串的Variant数组,因此无法转换为Worksheet
,Each
变量必须为Variant
。
Dim Assets As Variant
Dim Asset As Variant
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For Each Asset In Assets
'my code here
Sheets(Asset).Select
MsgBox ActiveSheet.Name 'test loop
Next Asset