2009-06-16 7 views
8

Ho un'app WinForm con altri moduli figlio (non mdi). Se l'utente preme "Esc", il modulo più in alto dovrebbe essere chiuso anche se non ha lo stato attivo.Come ottenere l'handle del modulo più in alto in un'app WinForm?

Posso usare un gancio per tastiera per catturare globalmente la fuga ma ho anche bisogno che la maniglia del modulo sia chiusa.

Suppongo che ci sia un modo per farlo utilizzando l'API Win32, ma esiste una soluzione che utilizza il codice gestito?

risposta

7

Ecco un modo per ottenere la forma più in alto che utilizza Win32 (non molto elegante, ma funziona):

public const int GW_HWNDNEXT = 2; // The next window is below the specified window 
public const int GW_HWNDPREV = 3; // The previous window is above 

[DllImport("user32.dll")] 
static extern IntPtr GetTopWindow(IntPtr hWnd); 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool IsWindowVisible(IntPtr hWnd); 

[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetWindow", SetLastError = true)] 
public static extern IntPtr GetNextWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.U4)] int wFlag); 

/// <summary> 
/// Searches for the topmost visible form of your app in all the forms opened in the current Windows session. 
/// </summary> 
/// <param name="hWnd_mainFrm">Handle of the main form</param> 
/// <returns>The Form that is currently TopMost, or null</returns> 
public static Form GetTopMostWindow(IntPtr hWnd_mainFrm) 
{ 
    Form frm = null; 

    IntPtr hwnd = GetTopWindow((IntPtr)null); 
    if (hwnd != IntPtr.Zero) 
    { 
     while ((!IsWindowVisible(hwnd) || frm == null) && hwnd != hWnd_mainFrm) 
     { 
      // Get next window under the current handler 
      hwnd = GetNextWindow(hwnd, GW_HWNDNEXT); 

      try 
      { 
       frm = (Form)Form.FromHandle(hwnd); 
      } 
      catch 
      { 
       // Weird behaviour: In some cases, trying to cast to a Form a handle of an object 
       // that isn't a form will just return null. In other cases, will throw an exception. 
      } 
     } 
    } 

    return frm; 
} 
1

FormCollection viene utilizzato dal l'oggetto Application per elencare i moduli attualmente aperti in un'applicazione tramite la proprietà OpenForms

Vedi http://msdn.microsoft.com/en-us/library/system.windows.forms.application.openforms.aspx

allora si potrebbe verificare TopMost() di proprietà di ogni modulo. E quando trovi la forma più alta, la chiudi.

+2

Purtroppo la proprietà Form.TopMost ottiene o imposta un valore che indica se il modulo deve essere visualizzato come modulo più in alto. Questo non mi dice se il modulo è in cima. – tzup

1

È possibile implementare un modello simile a un singleton nella forma più in alto e fornire una proprietà statica che restituisce l'istanza di se stessa e semplicemente chiuderla.

public class MainForm : Form 
    { 
     private static MainForm mainForm; 

     public static MainForm { get { return mainForm; } } 

     public MainForm() 
     { 
     mainForm = this; 
     } 
    } 


    // When the ESC key is pressed... 
    MainForm.MainForm.Close(); 
+0

Penso che abbiate frainteso la domanda. Immagina un'app WinForm con un modulo principale ingrandito e molti altri moduli più piccoli che si sovrappongono al modulo principale. Ogni volta che premi Esc il modulo più in alto dovrebbe chiudersi (tieni presente che potrebbe non avere il focus). Spero che questo chiarisca le cose. – tzup

+0

Penso che potresti aver frainteso la sua risposta. C'è solo un MainForm aperto alla volta, giusto? Il modello singleton presenta un handle statico per il modulo da qualsiasi punto dell'app, incluso il gancio della tastiera. –

+0

@Zachary Yates, l'esigenza è di essere in grado di chiudere le forme figlio, non la forma principale. – tzup

3

ne dite di questo utilizzando Application.Openforms

Form GetTopMostForm() 
{ 
    return Application.OpenForms 
     .Cast<Form>() 
     .First(x => x.Focused); 
} 
+0

Il requisito è chiudere il modulo che è in primo piano, che potrebbe non avere lo stato attivo. – tzup

2

lo so questo è un thread di 4 anni, ma ho avuto un problema simile e ho appena trovato una soluzione alternativa nel caso in cui qualcun altro incappasse in questa domanda e non volesse scherzare con le chiamate Win32.

Presumo che il modulo più in alto sarà quello che è stato attivato l'ultima volta. In questo modo è possibile conservare una raccolta separata di moduli, simile a Application.OpenForms, ad eccezione del fatto che questa raccolta sarebbe stata ordinata da quando ciascuno di essi è stato attivato l'ultima volta. Ogni volta che viene attivato un modulo, spostalo sul primo elemento della raccolta. Ogni volta che vedi il tasto ESC, devi chiudere la raccolta [0] e rimuoverlo.

+2

Oppure usa uno stack, è più naturale di una collezione per questo –