2013-12-10 7 views
6

Widzę zdarzenia WindowActivate uruchamiane na różnych poziomach, kiedy przełączam się między oknami w programie Excel, ale czy istnieje sposób na wywołanie zdarzenia, gdy program Excel staje się aplikacja na pierwszym planie? Jeśli kliknę poza programem Excel i pracuję, na przykład w przeglądarce przez pewien czas, a następnie kliknę z powrotem w oknie programu Excel, nie widzę żadnych zdarzeń. Czy istnieje sposób na wykrycie tego?Wykrywanie (w VBA), gdy okno zawierające wersję Excela staje się aktywne

Chciałbym odświeżyć niektóre elementy mojej aplikacji VBA, ponieważ czasami okazuje się, że moja funkcja Mouse Over oparta na funkcji Hypertext, traci zdolność do aktywowania wykresów. Mogę to naprawić, nie chroniąc i nie chroniąc arkusza kalkulacyjnego, lub przez niszczenie i ponowne inicjowanie podzbioru moich obiektów. Chciałbym wywołać tę akcję w przypadku, którego szukam.

Mogę to zrobić również za pomocą SendKeys, ale nie jest to dobre, ponieważ usuwa ustawienia klawiatury (na przykład blokadę przewijania) ze względu na udokumentowany błąd w SendKeys i powoduje, że ekran migocze bardziej, niż bym chciał.

Ponieważ kod będzie znajdować się w VBA, ograniczyłbym działanie do konkretnego skoroszytu. Jeśli inny (pasywny) skoroszyt jest aktywny podczas wchodzenia do okna instancji programu Excel, wówczas żadne działanie nie zostanie uruchomione i można użyć zdarzenia WorkbookActivate w celu odświeżenia aplikacji, jeśli i kiedy użytkownik wybierze skoroszyt zawierający ją.

+0

Do tego celu można użyć zdarzenia na poziomie aplikacji. Zobacz [Pearson] (http://www.cpearson.com/excel/appevent.aspx). Użycie "WorkbookActivate" będzie oznaczało, gdy aktywny jest dowolny skoroszyt w tym wystąpieniu. Jeśli użyjesz tego kodu w dodatku, będzie działał w dowolnej instancji (ale także będzie oznaczał, kiedy karta pomiędzy skoroszytami w tej instancji). – brettdj

+0

A także, co powinno się stać, gdy aktywowana zostanie pusta aplikacja Excel? Czy nie ma otwartych skoroszytów? Co dokładnie próbujesz osiągnąć? –

+0

@brettdj OK, dzięki. Wypróbowałem już wydarzenie WorkbookActivate w ThisWorkBook i pomyślałem, że będzie to to samo, co sugerowana wersja poziomu aplikacji. Po prostu wypróbowałem twoją sugestię, ale otrzymałem ten sam wynik: zdarzenie Application WorkbookActivate również się nie uruchomiło. Wrzuciłem go do modułu klasy i sprawdziłem, czy inne zdarzenia aplikacji działają poprawnie, więc wygląda na to, że nie robi to, co chcę. –

Odpowiedz

2

Wierzę, że to nie jest przewidziane w programie Excel bezpośrednio, więc korzystać z interfejsu API systemu Windows. Możesz zrobić programowanie win32 w VBA!

Wyjaśnienie

Można użyć funkcji API win32 SetWinEventHook aby system Windows zgłosić pewne zdarzenia do ciebie. W tym EVENT_SYSTEM_FOREGROUND, który jest wyzwalany, gdy zmienia się okno pierwszego planu. W poniższym przykładzie sprawdzam identyfikator procesu nowego okna przed identyfikatorem procesu Excela. Jest to prosty sposób, aby to zrobić, ale wykryje inne okna Excela, takie jak okno VBA, tak samo jak główne okno Excela. To może być lub może nie być zachowanie, które chcesz i może być odpowiednio zmienione.

Musisz zachować ostrożność używając SetWinEventHook, ponieważ przekazujesz do niego funkcję zwrotną. Ogranicza się to, co można zrobić w tej funkcji oddzwaniania, istnieje poza normalnym wykonaniem VBA, a wszelkie błędy w nim zawarte spowodują awarię programu Excel w nieporządek i nie do odzyskania.

Dlatego używam Application.OnTime do zgłaszania zdarzeń. Nie występują one w kolejności, gdy wiele zdarzeń jest uruchamianych szybciej niż aktualizacja Excel i VBA. Ale jest bezpieczniej. Można również zaktualizować kolekcję lub tablicę zdarzeń, a następnie odczytać je osobno poza wywołaniem zwrotnym WinEventFunc.

Przykład kodu

Aby to przetestować, tworzenia nowego modułu i przenieść ten kod do niego. Następnie uruchom StartHook. Pamiętaj, aby uruchamiać StopAllEventHooks przed zamknięciem programu Excel lub zmodyfikowaniem kodu !! W kodzie produkcyjnym prawdopodobnie dodasz StartEventHook i StopAllEventHooks do zdarzeń WorkBook_Open i WorkBook_BeforeClose, aby upewnić się, że działają w odpowiednim czasie. Pamiętaj, jeśli coś stanie się z kodem VBA WinEventFunc przed zatrzymaniem haka Program Excel zawiedzie się. Obejmuje to modyfikowany kod lub skoroszyt, w którym jest przechowywany. Również nie naciśnij przycisk zatrzymania w VBA, gdy hak jest aktywny. Przycisk zatrzymania może wyczyścić bieżący stan programu!

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 
Powiązane problemy