当单元格值由于公式而不是由用户改变时macros启动

我希望我的macros可以在包含公式的单元格中的值发生变化时启动。 即用户正在修改另一个单元,从而改变所讨论单元的值。

我注意到,使用语句(在这里find)只适用于用户修改单元格本身,但不是如果单元格自动更改 – 由于上面指定的公式。

Private Sub Worksheet_Change(ByVal Target As Range) If Not Intersect(Target, Range("A20")) Is Nothing Then ... 

有什么想法吗??

我试图按照这个问题的答案“ 自动执行一个单元格更改Excelmacros ”,但它没有工作…

提前致谢 :)

可能的解决方法来自于以下事实:要更改值,用户需要首先更改select。 所以我会:

1)在WS源代码模块的顶部声明一个名为“oldValue”的全局variables:

 Dim oldValue As Variant 

2)在用户键入任何东西之前注册公式的旧值(假设它在范围(“A4”),我让你适应其他):

 Private Sub Worksheet_SelectionChange(ByVal Target As Range) oldValue = Range("A4") End Sub 

3)检查更改是否影响更改事件中的公式:

 Private Sub Worksheet_Change(ByVal Target As Range) If Range("A4") <> oldValue Then MsgBox "User action has affected your formula" End If End Sub 

我已经testing了一个简单的总和,我能够写入没有任何提示的单元格,但如果我触及总和中涉及的单元格之一,MsgBox将显示出来。 我让你适应多种情况,用户添加/删除行(在这种情况下,我build议命名包含您想要跟踪的公式范围)和工作表引用。

编辑 我想立即做,而不是通过两个过程,这是可能的吗? 问题是我的macros涉及一个包含多个单元格的范围,所以很难为10个单元格存储旧的值。

如果范围是彼此相邻的,那么可以使用一个集合来代替使用variables:

 Dim oldValues As New Collection Private Sub Worksheet_SelectionChange(ByVal Target As Range) For j = 1 To 10 oldValues.Add Range("A" & j).Value Next j End Sub Private Sub Worksheet_Change(ByVal Target As Range) For j = 1 To 10 If Range("A" & j).Value <> oldValues(j) Then MsgBox "The value of Range(A" & j & ") has changed" End If Next j End Sub 

当然,如果范围不是彼此靠近,那么您可以将它们存储在SelectionChange事件中,如下所示:

 oldValues.Add Range("A1").Value oldValues.Add Range("B7").Value '... 

如果你只做了一次,只有10个范围,这应该是你的问题的一个合理的解决scheme。

你说:“只要包含公式单元格中的值发生变化 ,我就希望我的macros启动…”

如果只要包含公式的单元格被重新计算 (这不完全是您所要求的),则可以创build一个VBA函数,该函数只是简单地返回传递给它的值, 另外还可以做任何您想要执行的操作当公式重新计算时…

 Public Function Hook(ByVal vValue As Variant) As Variant Hook = vValue ' Add your code here... End Function 

…然后在调用这个函数的时候“包装”你的公式。 例如,如果您感兴趣的公式是=A1+1 ,您可以将其更改为=Hook(A1+1) ,并且在重新计算A1+1时将调用Hook函数(例如,当值A1改变)。 但是,重新计算A1+1可能会得到相同的结果,并仍然调用Hook函数(例如,如果用户在A1重新input相同的值),则可能会发生这种情况。

你可以去这里:

首先,在模块代码中声明一个公共variables

 Public r As Range, myVal '<~~ Place it in Module 

其次,在Workbook_Open事件中初始化你的variables。

 Private Sub Workbook_Open() Set r = Sheet1.Range("C2:C3") '<~~ Change to your actual sheet and range myVal = Application.Transpose(r) End Sub 

最后,设置你的Worksheet_Calculate事件。

 Private Sub Worksheet_Calculate() On Error GoTo halt With Application .EnableEvents = False If Join(myVal) <> Join(.Transpose(r)) Then MsgBox "Something changed in your range" '~~> You put your cool stuff here End If myVal = .Transpose(r) forward: .EnableEvents = True End With Exit Sub halt: MsgBox "Error " & Err.Number & ": " & Err.Description Resume forward End Sub 

当C2:C3的值发生变化时,上面将触发事件。
不是非常整洁,但在侦测目标范围内的变化。 HTH。

声明一个像Matteo这样的模块级variables绝对是一个好方法。

布赖恩的答案是在正确的轨道上保持所有的代码在同一个地方,但它缺less一个关键部分: Application.Caller

当在由单个单元格调用的函数中使用时, Application.Caller将返回该单元格的Range对象。 通过这种方式,可以在调用函数时将旧值存储在函数本身中,然后一旦计算完新值,就可以将其与旧值进行比较,并根据需要运行更多代码。

编辑:与Application.Caller的优点是解决scheme扩大本身,不会改变目标单元格如何安排(即连续与否)。