Excel VBA宏似乎在进行异步循环
我有一个绑定到特定工作表的VBA宏,其结构如下(子程序开始处对所有变量都进行了Dim的声明)。紧随 'Debugging' 的行是我在排查循环怪异行为时添加的调试代码。
For Each checkKey In checkValues.Keys
' The keys are cell addresses in the active sheet. Generally, a vertical
' stack of cells ordered from top to bottom
newValue = Range(checkKey).value
If checkValues(checkKey) <> newValue Then
' Update the value
checkValues(checkKey) = newValue
' Debugging
Range(checkKey).Offset(0, 6) = 0
' Now perform some calculations
someValue = SomeFunction(newValue)
' Debugging
Range(checkKey).Offset(0, 6) = 1
' Fill in the results of the calculation
Range(checkKey).Offset(0, 1).value = someValue("Part1")
Range(checkKey).Offset(0, 2).value = someValue("Part2")
' Debugging
Range(checkKey).Offset(0, 6) = 2
' Perform some more calculations
anotherValue = AnotherFunctin(newValue)
' Debugging
Range(checkKey).Offset(0, 6) = 3
' Fill in the results of the calculation
Range(checkKey).Offset(0, 3).value = anotherValue("Part1")
Range(checkKey).Offset(0, 4).value = anotherValue("Part2")
' Debugging
Range(checkKey).Offset(0, 6) = 4
End If
Next checkKey
我看到的情况是:假设checkValues列表中有10个单元格,形成一个连续的纵向块。我本来预期看到的是,顶端那个单元格右侧的单元格都会按照计算更新,看着“debug cell”的计数从0 增到4。然后是下一行往下的右侧单元格,再下一行往下的右侧单元格,依此类推。
但实际并非如此。相反,整组“debug cells”都被设为零,好像循环只执行了整段循环的前几行。随后,数值单元格开始缓慢更新(它们的计算相对较长),从底部向上更新。每处理一行时,其对应的“debug cell”也相应地从1 继续数到4。
几乎可以说,每次循环迭代时,VBA引擎都会执行到某个点就停下来,把该次迭代的状态压入一个栈中,然后进入下一次迭代。之后它再从栈中逐个取出,按相反的顺序完成。
有没有人遇到过这样的行为?有没有办法禁用这种行为,让循环在进入下一次迭代之前完成当前的每一次迭代?
解决方案
我终于搞清楚了发生了什么:问题中的代码是由工作表上的“更改”事件触发的,因此只要某个单元格发生变化,它就必须检查自己关心的单元格是否受到影响。
背景说明:它不能使用Target参数,因为它关心的单元格包含公式。对于包含公式的单元格,其结果变化并不会触发更改事件——只有你直接改变该单元格的内容时才会触发。
对change事件的早期研究(AI——去想想看!)让我以为它只在手动修改时显式触发。所以,当事件代码本身修改一个单元格时,这本不应该触发change事件。
结果证明那是不正确的。无论是手动还是通过脚本,只要单元格的内容被修改,change事件就会被触发。
我的子程序中的第一条对单元格的修改语句(向“debug cell”写入0)触发了一个改变事件,使子例程重新进入,等等。这就是为什么最初所有的“debug cells”都回到初始值0。随后,最后一个子例程(确实有点像一个栈)得以完成,依次让倒数第二个完成,然后再前一个,直到回到第一个。
解决办法是在子例程执行期间禁用事件触发,然后在结束后重新启用它们。