Coo*_*lue 8 excel vba excel-vba
当我在excel中的窗口之间切换时,我可以看到WindowActivate事件在不同级别触发,但是当excel成为前台应用程序时有没有办法触发事件?如果我单击excel并工作,例如在浏览器中工作一段时间,然后单击返回到Excel窗口,我看不到任何事件触发.有没有办法检测到这个?
我想刷新我的VBA应用程序的一些元素,因为偶尔我会发现基于超文本函数的鼠标悬停功能失去了激活图表的能力.我可以通过取消保护和保护工作表,或通过废弃和重新初始化我的对象的子集来解决它.我想在我正在寻找的事件上触发此操作.
我也可以通过SendKeys做到这一点,但它并不好,因为它消除了键盘设置(例如滚动锁定),因为SendKeys中记录的错误,它使屏幕闪烁超过我想要的.
由于代码将驻留在VBA中,因此我会将操作限制为特定的工作簿.如果在进入Excel实例窗口时激活了另一个(被动)工作簿,则不会触发任何操作,并且当用户选择包含它的工作簿时,我可以使用WorkbookActivate事件刷新应用程序.
我相信这不是直接在 Excel 中提供的,所以使用 Windows API。你可以在VBA中进行win32编程!
可以使用 win32 api 函数SetWinEventHook来让 Windows 向您报告某些事件。包括前台窗口变化时触发的EVENT_SYSTEM_FOREGROUND。在下面的示例中,我根据 Excel 的进程 ID 检查新的前台窗口的进程 ID。这是一种简单的方法,但它会检测其他 Excel 窗口,例如与主 Excel 窗口相同的 VBA 窗口。这可能是也可能不是您想要的行为,并且可以相应地进行更改。
您必须小心使用 SetWinEventHook,因为您将回调函数传递给它。您可以在此回调函数中执行的操作受到限制,它存在于 VBA 的正常执行之外,并且其中的任何错误都会导致 Excel 以不可恢复的方式崩溃。
这就是我使用 Application.OnTime 报告事件的原因。如果多个事件的触发速度比 Excel 和 VBA 更新更快,则它们不会按顺序发生。但它更安全。您还可以更新事件集合或数组,然后在 WinEventFunc 回调之外单独读取这些事件。
要对此进行测试,请创建一个新模块并将此代码粘贴到其中。然后运行 StartHook。记得在关闭 Excel 或修改代码之前运行 StopAllEventHooks!!在生产代码中,您可能会向 WorkBook_Open 和 WorkBook_BeforeClose 事件添加 StartEventHook 和 StopAllEventHooks,以确保它们在适当的时间运行。请记住,如果在挂钩停止之前 WinEventFunc VBA 代码发生了某些事情,Excel 将崩溃。这包括被修改的代码或它所在的工作簿被关闭。也不要没有按在VBA停止按钮,而钩处于活动状态。停止按钮可以擦除当前程序状态!
Option Explicit
Private Const EVENT_SYSTEM_FOREGROUND = &H3&
Private Const WINEVENT_OUTOFCONTEXT = 0
Private Declare Function SetWinEventHook Lib "user32.dll" (ByVal eventMin As Long, ByVal eventMax As Long, _
ByVal hmodWinEventProc As Long, ByVal pfnWinEventProc As Long, ByVal idProcess As Long, _
ByVal idThread As Long, ByVal dwFlags As Long) As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private pRunningHandles As Collection
Public Function StartEventHook() As Long
If pRunningHandles Is Nothing Then Set pRunningHandles = New Collection
StartEventHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0&, AddressOf WinEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT)
pRunningHandles.Add StartEventHook
End Function
Public Sub StopEventHook(lHook As Long)
Dim LRet As Long
If lHook = 0 Then Exit Sub
LRet = UnhookWinEvent(lHook)
End Sub
Public Sub StartHook()
StartEventHook
End Sub
Public Sub StopAllEventHooks()
Dim vHook As Variant, lHook As Long
For Each vHook In pRunningHandles
lHook = vHook
StopEventHook lHook
Next vHook
End Sub
Public Function WinEventFunc(ByVal HookHandle As Long, ByVal LEvent As Long, _
ByVal hWnd As Long, ByVal idObject As Long, ByVal idChild As Long, _
ByVal idEventThread As Long, ByVal dwmsEventTime As Long) As Long
'This function is a callback passed to the win32 api
'We CANNOT throw an error or break. Bad things will happen.
On Error Resume Next
Dim thePID As Long
If LEvent = EVENT_SYSTEM_FOREGROUND Then
GetWindowThreadProcessId hWnd, thePID
If thePID = GetCurrentProcessId Then
Application.OnTime Now, "Event_GotFocus"
Else
Application.OnTime Now, "Event_LostFocus"
End If
End If
On Error GoTo 0
End Function
Public Sub Event_GotFocus()
Sheet1.[A1] = "Got Focus"
End Sub
Public Sub Event_LostFocus()
Sheet1.[A1] = "Nope"
End Sub
Run Code Online (Sandbox Code Playgroud)