2013-12-10 2 views
6

Я могу видеть, как события WindowActivate запускаются на разных уровнях, когда я переключаюсь между окнами в excel, но есть способ запустить событие, когда excel становится переднего плана? Если я выйду из excel и работаю, например, в браузере некоторое время, а затем вернусь к окну excel, я не вижу никаких обстрелов событий. Есть ли способ обнаружить это?Обнаружение (в VBA), когда окно, содержащее экземпляр excel, становится активным

Я хотел бы обновить некоторые элементы моего приложения VBA, потому что иногда я нахожу, что моя функция Mouse Over, основанная на Hypertext Function, теряет способность активировать диаграммы. Я могу исправить это, не защищая и защищая рабочий лист, или путем перебора и повторной инициализации подмножества моих объектов. Я хотел бы вызвать это действие на мероприятии, которое я ищу.

Я также могу сделать это с помощью SendKeys, но это не приятно, потому что оно вытирает настройки клавиатуры (например, блокировку прокрутки) из-за документированной ошибки в SendKeys, и это делает мерцание экрана больше, чем хотелось бы.

Поскольку код будет находиться в VBA, я ограничу действие конкретной книгой. Если при входе в окно экземпляра Excel активна другая (пассивная) книга, никаких действий не будет инициировано, и я могу использовать событие WorkbookActivate для обновления приложения, если и когда пользователь выбирает книгу, содержащую ее.

+0

Для этого вы можете использовать мероприятие уровня приложения. См. [Pearson] (http://www.cpearson.com/excel/appevent.aspx). Использование «WorkbookActivate» будет отображаться при активации любой книги в этом экземпляре. Если вы используете этот код в аддине, он будет работать в любом экземпляре (но также он будет отображаться при вставке между книгами в этом экземпляре). – brettdj

+0

А также, что должно произойти, если запущено пустое приложение excel? то есть нет книг, открытых? Что именно вы пытаетесь достичь? –

+0

@brettdj ОК, спасибо. Я уже пробовал событие WorkbookActivate в ThisWorkBook, и я подумал, что это будет то же самое, что и предлагаемая вами версия уровня приложения. Я просто попробовал ваше предложение, но получил тот же результат: событие Application WorkbookActivate также не срабатывало. Я поместил его в модуль класса и проверил, что другие события приложения работают нормально, поэтому похоже, что он не делает то, что я хочу. –

ответ

2

Я считаю, что это не предусмотрено в 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 
Смежные вопросы