当单元格值由于公式而非用户而发生更改时,宏启动

时间:2014-12-18 17:19:50

标签: excel vba excel-vba

我希望只要包含公式的单元格中的值发生更改,我的宏就会启动。 即,用户正在修改另一个小区,从而改变了所讨论的小区的值。

我注意到使用声明(在此处找到),只有在用户修改单元格本身时才有效,但如果单元格自动更改则无效 - 由于上面指定的公式。

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

有什么想法吗?

我尝试按照这个问题的答案" automatically execute an Excel macro on a cell change"但它不起作用......

提前致谢:)

4 个答案:

答案 0 :(得分:1)

可能的解决方法是,为了更改值,用户需要首先更改选择。所以我会:

1)声明一个名为" oldValue"的全局变量。在WS源代码模块之上:

Dim oldValue As Variant

2)在用户输入任何内容之前注册公式的旧值(让我们在范围中说出它(" A4"),我让你适应其他人) :

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

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

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

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

编辑 我想立即执行此操作,而不是通过2个进程,是否可能?问题是我的宏涉及一个包含多个单元格的范围,因此很难存储10个单元格的旧值。

如果范围彼此相邻,那么您可以使用集合代替使用变量:

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
'...

如果你完成了这个ONCE,只有10个范围,它应该是你的问题的合理解决方案。

答案 1 :(得分:1)

你说,“每当包含公式的单元格中的值发生变化时,我希望我的宏启动...”

如果在重新计算包含公式的单元格时运行代码(这不是您要求的那样),一个解决方案可能是创建一个VBA函数,该函数只返回传递给该值的值它, plus 做重新计算公式时你想做的其他事情......

Public Function Hook(ByVal vValue As Variant) As Variant
    Hook = vValue

    ' Add your code here...
End Function

...然后在调用此函数时“包装”您的公式。例如,如果您感兴趣的公式为=A1+1,则会将其更改为=Hook(A1+1),并且只要重新计算Hook,就会调用A1+1函数(例如,当A1中的值发生变化时)。但是,重新计算A1+1可能会产生相同的结果并仍然调用Hook函数(例如,如果用户在A1中重新输入相同的值)。

答案 2 :(得分:1)

声明像Matteo描述的模块级变量绝对是一个很好的方法。

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

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

编辑:Application.Caller的优点是解决方案本身就可以扩展,并且无论目标细胞的排列方式如何(即连续或不连续)都不会改变。

答案 3 :(得分:1)

你可以去看看:

首先,在模块代码中声明公共变量

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

其次,在 Workbook_Open 事件中初始化变量。

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。