【问题标题】:Autorun Excel vba code when cell value changes [duplicate]单元格值更改时自动运行 Excel vba 代码
【发布时间】:2015-10-23 06:30:34
【问题描述】:

我正在寻找一种在单元格值为 0 时自动启动某个 Sub 的方法。

例如如果我在单元格 A1 中输入“0”,则应该运行以下子程序

 Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"

如果我在单元格 A1 中输入 1(或任何其他大于 0 的值),另一个 Sub 应该运行,例如

Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"

应该在我在 excel 中输入值后立即调用 Sub,而无需按下按钮或其他任何东西。 有没有办法做到这一点?

【问题讨论】:

  • 你试过worksheet_change吗?
  • Worksheet_Change 是您能做的最多的事情,但您至少必须按“回车”来触发事件。当用户按下某个键时,不可能触发事件。所以它不会在你按下 0 或 1 时立即发生
  • @MaximePorté 其实还有 application.onkey 和 windows API。
  • @findwindow 我不知道。你有在 Excel 中实现它的链接吗? (我打破了我的懒惰并谷歌它,这是第一个链接:msdn.microsoft.com/en-us/library/office/ff197461.aspx
  • THIS 将帮助您开始

标签: vba excel


【解决方案1】:

让我们从这段代码开始,我将在下面解释。

打开 VB 编辑器 Alt+F11。右键单击您希望发生此行为的工作表,然后选择 View Code

将以下代码复制并粘贴到工作表代码中。

Private Sub Worksheet_Change(ByVal Target As Range)
        'CountLarge is an Excel 2007+ property, if using Excel 2003 
        'change to just Count
        If Target.Cells.CountLarge > 1 Or IsEmpty(Target) Then Exit Sub

        If Target.Address = "$A$1" Then
                If Target.Value = 0 Then
                        Me.Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"
                ElseIf Target.Value = 1 Then
                        Me.Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"
                End If
        End If

End Sub

每次用户更改工作表时都会触发Worksheet_Change 事件。例如,如果您更改单元格值,则会触发此事件。

此子例程中的第一行检查以确保没有更改多个单元格,并且实际上存在实际的单元格更改,如果其中任何一个不正确,则它将不会继续。

然后我们检查以确保单元格 A1 中发生了值更改,如果发生了,我们输入 IF 语句。

从那里,我们检查输入到单元格A1 中的值。如果值为 0,则将适当的公式添加到 H32。如果值为 1,则将适当的公式添加到 B15。如果在单元格 A1 中输入了 0 或 1 以外的值,则不会发生任何事情。

请务必注意,您必须离开单元格才能触发此事件,因此虽然这是一个好的开始,但我目前不知道有一种方法可以在不至少按 Enter 或离开的情况下触发此事件细胞。

更新

经过一番研究和玩弄,我想出了如何在不按 Enter 或任何其他按钮的情况下进行此更改,这将在按下“0”或“1”后立即发生,即使您正在编辑单元格值。我使用了来自this previous SO question 的键盘处理程序。

BEGIN KEYBOARD HANDLINGEND KEYBOARD HANDLING 事件之间的代码来自上面。

将以下代码复制并粘贴到要在其上捕获这些击键的任何工作表的工作表代码中:

Option Explicit
'BEGIN KEYBOARD HANDLING

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type MSG
    hwnd As Long
    Message As Long
    wParam As Long
    lParam As Long
    time As Long
    pt As POINTAPI
End Type

Private Declare Function WaitMessage Lib "user32" () As Long

Private Declare Function PeekMessage Lib "user32" _
Alias "PeekMessageA" _
(ByRef lpMsg As MSG, ByVal hwnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long) As Long

Private Declare Function TranslateMessage Lib "user32" _
(ByRef lpMsg As MSG) As Long

Private Declare Function PostMessage Lib "user32" _
Alias "PostMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) As Long

Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long

Private Const WM_KEYDOWN As Long = &H100
Private Const PM_REMOVE  As Long = &H1
Private Const WM_CHAR    As Long = &H102
Private bExitLoop As Boolean

Sub StartKeyWatch()

    Dim msgMessage As MSG
    Dim bCancel As Boolean
    Dim iKeyCode As Integer
    Dim lXLhwnd As Long

    'handle the ESC key.
    On Error GoTo errHandler:
    Application.EnableCancelKey = xlErrorHandler
   'initialize this boolean flag.
    bExitLoop = False
    'get the app hwnd.
    lXLhwnd = FindWindow("XLMAIN", Application.Caption)
    Do
        WaitMessage
        'check for a key press and remove it from the msg queue.
        If PeekMessage _
            (msgMessage, lXLhwnd, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) Then
            'strore the virtual key code for later use.
            iKeyCode = msgMessage.wParam
           'translate the virtual key code into a char msg.
            TranslateMessage msgMessage
            PeekMessage msgMessage, lXLhwnd, WM_CHAR, _
            WM_CHAR, PM_REMOVE
           'for some obscure reason, the following
          'keys are not trapped inside the event handler
            'so we handle them here.
            If iKeyCode = vbKeyBack Then SendKeys "{BS}"
            If iKeyCode = vbKeyReturn Then SendKeys "{ENTER}"
           'assume the cancel argument is False.
            bCancel = False
            'the VBA RaiseEvent statement does not seem to return ByRef arguments
            'so we call a KeyPress routine rather than a propper event handler.
            Sheet_KeyPress _
            ByVal msgMessage.wParam, ByVal iKeyCode, ByVal Selection, bCancel
            'if the key pressed is allowed post it to the application.
            If bCancel = False Then
                PostMessage _
                lXLhwnd, msgMessage.Message, msgMessage.wParam, 0
            End If
        End If
errHandler:
        'allow the processing of other msgs.
        DoEvents
    Loop Until bExitLoop

End Sub

Sub StopKeyWatch()

    'set this boolean flag to exit the above loop.
    bExitLoop = True

End Sub

Private Sub Worksheet_Activate()
        Me.StartKeyWatch
End Sub

Private Sub Worksheet_Deactivate()
        Me.StopKeyWatch
End Sub

'End Keyboard Handling

Private Sub Sheet_KeyPress(ByVal KeyAscii As Integer, ByVal KeyCode As Integer, ByVal Target As Range, Cancel As Boolean)

        'CountLarge is an Excel 2007+ property, if using Excel 2003 
        'change to just Count
        If Target.Cells.CountLarge > 1 Or IsEmpty(Target) Then Exit Sub

        If Target.Address = "$A$1" Then
                If KeyAscii = 48 Then
                        Me.Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"
                ElseIf KeyAscii = 49 Then
                        Me.Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"
                End If
        End If

End Sub

另外,在ThisWorkbook对象上右击-->查看代码,将这段代码添加到:

Private Sub Workbook_Open()
        Sheets("Sheet1").StartKeyWatch
End Sub

请务必将 Sheet1 更改为您的工作表名称。

VBA 将“侦听”按键,如果活动单元格是 A1 并且输入了 0 或 1,则即使在用户执行任何其他操作之前也会执行适当的操作。

我要补充一点,他的性能代价很小,因为在 Workbook_Open 上执行的代码需要几秒钟才能运行。

感谢用户 Siddharth Rout 指出 Excel 2007 及更高版本中 Count 的潜在问题,并指导我改用 CountLarge

【讨论】:

  • Target.Cells.Count 在 Excel 2007+ 中可能会出现错误
  • 2007 年及以后?对我来说,这段代码在 Excel 2013 中运行良好。
  • OK 这样做:) 按 Ctrl A 以便选择工作表中的所有单元格,然后按 Delete 按钮。您可能需要阅读问题中标记的重复帖子中的说明。
  • 好的,我将更新为使用 CountLarge 并假设用户已超过 Excel 2003。
  • 哇,如果我知道我会被投票 4 次,我会发布答案 XD
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
  • 2018-09-05
  • 1970-01-01
  • 2017-09-17
  • 2013-12-15
相关资源
最近更新 更多