2009-06-16 11 views
8

Tengo una aplicación WinForm que tiene otras formas secundarias (no mdi). Si el usuario presiona "Esc", la forma más alta debe estar cerrada, incluso si no tiene el foco.¿Cómo obtener el control de la forma más alta en una aplicación WinForm?

Puedo usar un gancho de teclado para atrapar globalmente el Escape, pero también necesito el asa del formulario para cerrarlo.

Supongo que hay una manera de hacerlo usando la API de Win32, pero ¿hay alguna solución usando el código administrado?

Respuesta

7

Aquí es una manera de obtener el formulario de nivel superior que utiliza Win32 (no muy elegante, pero funciona):

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 es utilizado por el objeto Application para enumerar las formas abiertas actualmente en una aplicación a través de la propiedad OpenForms

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

A continuación, se puede comprobar TopMost (a) Propiedad de cada forma. Y cuando encuentras una forma superior, la cierras.

+2

Desafortunadamente, la propiedad Form.TopMost obtiene o establece un valor que indica si el formulario se debe mostrar como una forma superior. Esto no me dice si el formulario ES superior. – tzup

1

Puede implementar un patrón tipo singleton en su forma más alta y proporcionar una propiedad estática que devuelva la instancia de sí mismo y simplemente cerrarla.

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

Creo que malinterpretaste la pregunta. Imagine una aplicación WinForm con un formulario principal maximizado y muchas otras formas más pequeñas en cascada sobre el formulario principal. Cada vez que presione Esc, la forma más alta debería cerrarse (tenga en cuenta que puede no tener el foco). Espero que esto aclare las cosas. – tzup

+0

Creo que es posible que haya entendido mal su respuesta. Solo hay un MainForm abierto a la vez, ¿no? El patrón singleton presenta un identificador estático al formulario desde cualquier lugar de la aplicación, incluido el gancho del teclado. –

+0

@Zachary Yates, el requisito es poder cerrar formularios secundarios, no la forma principal. – tzup

3

¿Qué tal esto utilizando Application.Openforms

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

El requisito es cerrar el formulario que está más arriba, que podría no tener el foco. – tzup

2

que sé este es un hilo de hace 4 años, pero tuve un problema similar y simplemente se me ocurrió una solución alternativa en caso de que alguien más se tropiece con esta pregunta y no quiera perder el tiempo con las llamadas de Win32.

Supongo que la forma más alta será la que se activó por última vez. Por lo tanto, podría mantener una colección de formularios separada, similar a Application.OpenForms, excepto que esta colección se ordenaría cuando se activara por última vez. Siempre que se active un formulario, muévalo al primer elemento de la colección. Cada vez que vea la tecla ESC, cerrará la colección [0] y la eliminará.

+2

O usa una pila, es más natural que una colección para esto –

Cuestiones relacionadas