2012-03-21 6 views
7

Ho un'app Delphi 2006 che può aprire una finestra di dialogo modale in risposta a una condizione di errore. Sembra entrare in uno stato in cui una di queste finestre di dialogo modali è aperta, posizionata di fronte al modulo principale, ma nessuna delle due forme risponde ai messaggi. Cliccando su uno dei due dà un "bonk". L'app sta funzionando bene, l'interfaccia utente sta aggiornando il modulo principale, ma non puoi fare nulla. Immagino che ci sia molto probabilmente un'altra finestra di dialogo modale sotto il form principale. Che sia uno dei miei o uno di Windows, non ne ho idea.Delphi - come faccio a sapere quale finestra di dialogo modale ha il focus e portarlo in primo piano?

Altri punti:

  • l'applicazione risponde alle scorciatoie da tastiera OK. Una di queste scorciatoie blocca l'applicazione con grazia e questo ha funzionato. Da allora non sono riuscito a riprodurre la situazione.
  • l'app ha un'icona nella barra delle applicazioni. Questo risponde ai clic del tasto destro del mouse. Se minimizzo l'app da qui, il modulo principale si riduce e lascia la finestra di dialogo modale visualizzata, ancora senza messa a fuoco. Se ripristino il modulo principale, le cose sono come erano, senza che nessuna finestra abbia il focus. Alt-tab ha risultati simili.
  • piattaforma è Windows 7
  • chiamo DisableProcessWindowsGhosting prima di eventuali forme sono create
  • apro le finestre di dialogo modali con

    ModalDialog.PopupParent := MainForm ; 
    ModalDialog.ShowModal ; 
    
  • posticipo queste finestre di dialogo di errore se le altre finestre di dialogo modali sono aperti:

    if (Application.ModalLevel = 0) then 
        {open modal dialog} 
    

Il mio qu estion ha due parti:

C'è un modo per scoprire a livello di programmazione quale finestra è attiva? Potrei quindi intervenire in questo scenario o in ultima istanza potrei fornire un tasto di scelta rapida per portarlo in primo piano o prendere qualche azione evasiva (a seconda della finestra di dialogo) come impostare ModalResult su mrCancel.

Come può presentarsi questa situazione? Normalmente quando ottengo una finestra di dialogo modale dietro il modulo principale (posso farlo aprendo la finestra di dialogo modale, riducendo l'app dall'icona nella barra delle applicazioni, quindi ripristinando di nuovo l'app - il modulo principale dell'applicazione viene ripristinato davanti alla finestra di dialogo, con la finestra di dialogo mantiene ancora l'attenzione), posso riportarla in primo piano facendo clic sull'icona della barra delle applicazioni, oppure chiuderla con la chiave Esc ma in questo caso non ha funzionato.

** AGGIORNAMENTO * *

correzione di Misha ha lavorato a parte le finestre di dialogo non-Delphi come TSaveDialog. Sono stato in grado di farli funzionare anche aggiungendo Application.ModalPopupMode := pmAuto ; poco prima della chiamata a Execute.

Con "preso a lavorare" intendo che la finestra di salvataggio era di fronte dopo la seguente sequenza:

  • Apri Salva dialogo
  • minimizzare app dalla tray icon
  • ripristino app dalla tray icon

mentre era dietro la forma principale senza il ModalPopupMode := pmAuto.

Quindi spero che questi cambiamenti aiuteranno il problema (ancora non riprodotto).

+2

Scoprire quale finestra è concentrarsi e di adottare provvedimenti correttivi non ha intenzione di aiutare. Hai bisogno di affrontare il problema alla fonte. Ciò significa comprendere la proprietà della finestra (ad esempio PopupParent). –

+0

Grazie a @David.Scoprire quale finestra ha il focus sarà sicuramente di aiuto in quanto mi mostrerà come posso riprodurre il problema, cosa che non posso fare al momento. – rossmcm

+0

Una volta che hai manifestato dovresti essere in grado di usare qualcosa come Spy ++ per capire i rapporti di proprietà. Proverò a eseguire il debug di questo dall'esterno, almeno finché non avrai una riproduzione. –

risposta

4

L'ultima finestra pop-up attiva (VCL o no) possono essere interrogati con GetLastActivePopup:

function GetTopWindow: HWND; 
begin 
    Result := GetLastActivePopup(Application.Handle); 
    if (Result = 0) or (Result = Application.Handle) or 
     not IsWindowVisible(Result) then 
    Result := Screen.ActiveCustomForm.Handle; 
end; 

Questo è un po ' copiata da TApplication.BringToFront.

Portando questa finestra in primo piano può essere fatto da SetForegroundWindow:

SetForegroundWindow(GetTopWindow); 

Nota che Application.BringToFront potrebbe fare il trucco del tutto, ma una volta che ho provato non ha funzionato correttamente, una situazione non sono stato in grado di riproducono dal però.

+1

È necessario selezionare 'Assigned (Screen.ActiveCustomForm)'. Sto eseguendo la mia app nel vassoio con 'Application.ShowMainForm: = False', se chiamo' GetTopWindow' quando nessun modulo ha mostrato ancora getta un AV. – saastn

0

GetForegroundWindow() è la funzione che si sta cercando, se si conosce il titolo o il manico della finestra modale è semplice.

HWND GetForegroundWindow();

recupera un handle alla finestra in primo piano (la finestra con cui l'utente sta lavorando ). Il sistema assegna una priorità leggermente superiore a al thread che crea la finestra in primo piano rispetto a in altri thread.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633505%28v=vs.85%29.aspx

5

Se un modulo che ha il focus impiega troppo tempo per rispondere ai messaggi (Form1), in modo che Windows pensa Form1 non risponde, e Form1 quindi visualizza un modulo modale (Form2), dopo Form2 è visualizzato e l'applicazione sta elaborando i messaggi nuovamente, Form1 verrà portato in primo piano, quindi potenzialmente "coprendo" Form2.

Mettendo questo in caso Application.OnIdle farà il trucco:

if Assigned(Screen.ActiveForm) then 
    begin 
    if (fsModal in Screen.ActiveForm.FormState) and 
     (Application.DialogHandle <= 0)) then 
    begin 
     Screen.ActiveForm.BringToFront; 
    end; 
    end; 
+0

Grazie a @Misha. Sembra che faccia il trucco, tranne quando è aperta una finestra di dialogo non VCL, ad es. una finestra di dialogo per il caricamento del file, quindi non siamo ancora arrivati. Ho usato un timer poiché l'evento OnIdle non si attiva (ad esempio) se è aperto un menu di scelta rapida – rossmcm

+0

Vedere OP per l'aggiornamento. – rossmcm

0

Ho usato la soluzione di Misha e ho lavorato un po 'oltre (usando il codice NGLN), per risolvere i problemi che Rossmcm ha visto (le finestre di dialogo non VCL di handlings).

Il seguente codice è in esecuzione ho un timer:

type 
    TCustomFormAccess = class(TCustomForm); 


if Assigned(Screen.ActiveCustomForm) then 
begin 
    if ((fsModal in Screen.ActiveCustomForm.FormState) and 
     (Application.DialogHandle <= 0)) then 
    begin 
    TopWindow := GetLastActivePopup(Application.Handle); 
    TopWindowForm := nil; 
    for i := 0 to Screen.CustomFormCount - 1 do 
    begin 
     CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]); 
     if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess; 
    end; 
    if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then 
    begin 
     Screen.ActiveCustomForm.BringToFront; 
    end; 
    end; 
end; 
Problemi correlati