2013-12-10 14 views
6

È possibile visualizzare gli eventi WindowActivate che si attivano, a vari livelli, quando si passa da una finestra all'altra di Excel, ma esiste un modo per attivare un evento quando excel diventa il applicazione in primo piano? Se clicco su excel e lavoro, ad esempio nel browser per un po 'e poi clic su di nuovo su una finestra di Excel, non vedo alcun evento che si attiva. C'è un modo per rilevare questo?Rilevamento (in VBA) quando la finestra contenente un'istanza excel diventa attiva

Vorrei aggiornare alcuni elementi della mia applicazione VBA perché, a volte, trovo che la funzione Mouse Over, basata sulla funzione Hypertext, perda la sua capacità di attivare i grafici. Posso aggiustarlo eliminando e proteggendo il foglio di lavoro, oppure eliminando e reinizializzando un sottoinsieme dei miei oggetti. Vorrei attivare questa azione per l'evento che sto cercando.

Posso anche farlo con SendKeys ma non è bello perché cancella le impostazioni della tastiera (ad esempio il blocco di scorrimento) a causa di un errore documentato in SendKeys e rende lo sfarfallio dello schermo più di quanto vorrei.

Poiché il codice risiederà in VBA, limitarei l'azione a una cartella di lavoro specifica. Se una cartella di lavoro (passiva) diversa è attiva quando si accede alla finestra dell'istanza di Excel, non verrà eseguita alcuna azione e posso utilizzare l'evento WorkbookActivate per aggiornare l'applicazione se e quando l'utente seleziona la cartella di lavoro che la contiene.

+0

È possibile utilizzare un evento a livello di applicazione per eseguire questa operazione. Vedi [Pearson] (http://www.cpearson.com/excel/appevent.aspx). L'uso di 'WorkbookActivate' contrassegna quando una cartella di lavoro in quell'istanza viene attivata. Se si utilizza questo codice in un addin, funzionerà in qualsiasi istanza (ma verrà contrassegnato anche quando si passa da una cartella all'altra all'interno di tale istanza). – brettdj

+0

E anche cosa dovrebbe accadere se viene attivata un'applicazione Excel vuota? non ci sono cartelle di lavoro aperte? Cosa stai cercando di ottenere esattamente? –

+0

@brettdj OK, grazie. Avevo già provato l'evento WorkbookActivate in ThisWorkBook e pensavo che sarebbe stato lo stesso della versione a livello di applicazione che suggerisci. Ho appena provato il tuo suggerimento ma ho ottenuto lo stesso risultato: l'evento Application WorkbookActivate non ha sparato neanche. L'ho messo in un modulo di classe e ho verificato che altri eventi dell'applicazione funzionavano correttamente, quindi sembra che non faccia quello che voglio. –

risposta

2

Credo che questo non è previsto in Excel direttamente, in modo da utilizzare l'API di Windows. Puoi programmare Win32 in VBA!

Spiegazione

È possibile utilizzare la funzione API Win32 SetWinEventHook per arrivare a Windows di segnalare determinati eventi a voi. Compreso EVENT_SYSTEM_FOREGROUND che viene attivato quando cambia la finestra in primo piano. Nell'esempio seguente, controllo l'ID del processo della nuova finestra in primo piano rispetto all'ID processo di Excel. Questo è un modo semplice per farlo, ma rileverà altre finestre di Excel come la finestra VBA allo stesso modo della finestra principale di Excel. Questo può o non può essere il comportamento che si desidera e può essere modificato di conseguenza.

È necessario fare attenzione utilizzando SetWinEventHook, poiché si passa a una funzione di richiamata. Sei limitato a ciò che puoi fare in questa funzione di callback, esiste al di fuori della normale esecuzione di VBA e qualsiasi errore al suo interno causerà l'arresto anomalo di Excel in modo irrecuperabile.

Ecco perché utilizzo Application.OnTime per segnalare gli eventi. Non sono autorizzati a verificarsi in ordine se più eventi vengono attivati ​​più rapidamente rispetto all'aggiornamento di Excel e VBA. Ma è più sicuro. È anche possibile aggiornare una raccolta o una serie di eventi, quindi leggerli di nuovo separatamente dal callback di WinEventFunc.

Esempio di codice

Per verificare ciò, creare un nuovo modulo e incollare questo codice in esso. Quindi eseguire StartHook. Ricordarsi di eseguire StopAllEventHooks prima di chiudere Excel o modificare il codice !! Nel codice di produzione probabilmente aggiungerai StartEventHook e StopAllEventHooks agli eventi WorkBook_Open e WorkBook_BeforeClose per assicurarti che vengano eseguiti nei momenti appropriati. Ricorda, se qualcosa accade con il codice VBA WinEventFunc prima che il blocco si blocchi Excel si bloccherà. Ciò include il codice in fase di modifica o la cartella di lavoro in cui si trova chiuso. Anche fare non premere il pulsante di arresto in VBA mentre un gancio è attivo. Il pulsante stop può cancellare lo stato attuale del programma!

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