Я считаю, что это не предусмотрено в Excel напрямую, поэтому использовать API Windows. Вы можете делать win32-программирование в VBA!
Объяснение
Вы можете использовать функцию SetWinEventHook апи win32, чтобы получить Windows, чтобы сообщить о некоторых событиях вам. Включая EVENT_SYSTEM_FOREGROUND, который запускается при изменении окна переднего плана. В приведенном ниже примере я проверяю идентификатор процесса нового переднего плана на идентификатор процесса Excel. Это простой способ сделать это, но он обнаружит другие окна Excel, такие как окно VBA, как и главное окно Excel. Это может быть или не быть поведением, которое вы хотите, и может быть соответствующим образом изменено.
Вы должны быть осторожны с помощью SetWinEventHook, поскольку вы передаете ему функцию обратного вызова. Вы ограничены тем, что вы можете сделать в этой функции обратного вызова, она существует вне обычного исполнения VBA, и любые ошибки внутри нее приведут к сбою в работе Excel с беспорядочным неисправимым способом.
Вот почему я использую Application.OnTime для сообщения о событиях. Они не выполняются, если несколько событий запускаются быстрее, чем Excel и обновление VBA. Но это безопаснее. Вы также можете обновить коллекцию или массив событий, а затем прочитать их обратно отдельно от обратного вызова WinEventFunc.
Пример кода
Чтобы проверить это, создайте новый модуль и вставьте этот код в него. Затем запустите StartHook. Не забудьте запустить StopAllEventHooks перед закрытием Excel или модификацией кода! В производственном коде вы, вероятно, добавляете StartEventHook и StopAllEventHooks к событиям WorkBook_Open и WorkBook_BeforeClose, чтобы обеспечить их запуск в соответствующие моменты времени. Помните, если что-то случится с кодом WinAventFunc 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
Для этого вы можете использовать мероприятие уровня приложения. См. [Pearson] (http://www.cpearson.com/excel/appevent.aspx). Использование «WorkbookActivate» будет отображаться при активации любой книги в этом экземпляре. Если вы используете этот код в аддине, он будет работать в любом экземпляре (но также он будет отображаться при вставке между книгами в этом экземпляре). – brettdj
А также, что должно произойти, если запущено пустое приложение excel? то есть нет книг, открытых? Что именно вы пытаетесь достичь? –
@brettdj ОК, спасибо. Я уже пробовал событие WorkbookActivate в ThisWorkBook, и я подумал, что это будет то же самое, что и предлагаемая вами версия уровня приложения. Я просто попробовал ваше предложение, но получил тот же результат: событие Application WorkbookActivate также не срабатывало. Я поместил его в модуль класса и проверил, что другие события приложения работают нормально, поэтому похоже, что он не делает то, что я хочу. –