KB0070:为什么启用 thinkcell 后,我的 Excel 宏运行缓慢?
VBA 宏中一个常见的性能问题来源是使用 .Select 函数。每次在 Excel 中选择一个单元格时,每个 Excel 加载项(包括 thinkcell)都会收到此选择更改事件的通知,这会显著降低宏的运行速度。使用宏录制器创建的宏尤其容易出现这类问题。
Microsoft 也建议在 VBA 代码中避免使用 .Select 语句,以提升性能:
- Office 博客:Excel VBA 性能编码最佳实践
- MSDN:Improving Performance in Excel 2007:"Reference Excel objects such as Range objects directly, without selecting or activating them"(位于 "Faster VBA Macros" 部分)。
示例:如何避免使用 .Select 语句
我们来看下面这个简单的宏 AutoFillTable:
Sub AutoFillTable()
Dim iRange As Excel.Range
Set iRange = Application.InputBox(prompt:="输入范围", Type:=8)
Dim nCount As Integer
nCount = iRange.Cells.Count
For i = 1 To nCount
Selection.Copy
If iRange.Cells.Item(i).Value = "" Then
iRange.Cells.Item(i).Range("A1").Select
ActiveSheet.Paste
Else
iRange.Cells.Item(i).Range("A1").Select
End If
Next
End Sub
此函数会打开一个输入框,要求用户指定单元格范围。 该函数会遍历该范围内的所有单元格。 如果找到非空单元格,它会将该单元格内容复制到剪贴板。 该函数会将剪贴板内容粘贴到后续每个空单元格中。
AutoFillTable 使用剪贴板复制单元格内容。 因此,该函数需要选择每个被操作的单元格,以便 Excel 知道从哪个单元格复制,以及应粘贴到哪个单元格。 推荐的解决方案如下方函数 AutoFillTable2 所示:
Sub AutoFillTable2()
Dim iRange As Excel.Range
Set iRange = Application.InputBox(prompt:="输入范围", Type:=8)
Dim iCellMaster As Excel.Range
For Each iCell In iRange.Cells
If iCell.Value = "" Then
If Not iCellMaster Is Nothing Then
iCellMaster.Copy (iCell)
End If
Else
Set iCellMaster = iCell
End If
Next iCell
End Sub
AutoFillTable2 与前一个函数有两个重要区别:
- 最重要的是,它不再需要选择单元格。 相反,当找到非空单元格时,该单元格会被存储在变量
iCellMaster中。 之后,每当找到空单元格时,iCellMaster的所有内容都会被复制到iCell中。 - 它使用 Visual Basic 语言功能
For Each … Next来访问该范围内的每个单元格。 这本身已经提升了可读性。