我正在尝试使用在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
,但是这破坏了代码。任何帮助表示赞赏,
干杯
最佳答案
您在问题中显示的代码由于以下原因而失败:
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
Assets
是一种工作表,是一种对象类型,为对象分配值时必须使用Set
:Set Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
这将失败,因为
Array("…")
不是工作表。您暗示可以运行早期版本的代码,但不会遍历工作表。原因是:
MsgBox ActiveSheet.Name
这将显示 Activity 工作表的名称,但是此循环中的任何内容都不会更改 Activity 工作表。
我对您的解决方案不满意,尽管没有任何明显的错误。我已经看到太多程序失败,因为程序员在单个语句中做了太多事情。首先,语句越复杂,首先需要花费较长的时间,而在随后的维护过程中需要花费的时间也就越长。有时,原始程序员会使该语句略有错误;有时,维护程序员在尝试更新时会出错。在每种情况下,程序员节省的额外时间都不能证明节省运行时间是合理的。
Alex K通过根据VBA的要求将
Assets
和Asset
重新定义为Variants并添加Sheets(Asset).Select
来更改 Activity 的工作表,从而修复了您的代码。我不赞成这样做,因为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
定义为Variants的原因。请注意,您的解决方案之所以有效,是因为工作表是一个对象。我使用了
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)
。这只是我(过于?过度)精确。希望这可以帮助。
关于vba - 遍历VBA中的指定工作表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27922453/