2009-06-16 20 views
8

J'ai une application WinForm qui a d'autres formes enfant (pas mdi). Si l'utilisateur appuie sur "Echap", le formulaire le plus haut devrait être fermé même s'il n'a pas le focus.Comment obtenir le handle du formulaire le plus élevé dans une application WinForm?

Je peux utiliser un crochet pour attraper globalement l'échappement, mais j'ai aussi besoin de fermer la poignée du formulaire.

Je suppose qu'il existe un moyen de le faire en utilisant l'API Win32, mais existe-t-il une solution utilisant le code managé?

Répondre

7

Voici une façon d'obtenir la plus haute forme qui utilise Win32 (pas très élégant, mais il fonctionne):

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 est utilisé par l'objet Application à la liste des formulaires actuellement ouverts dans une application par la OpenForms propriété

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

Ensuite, vous pouvez vérifier la propriété TopMost() de chaque forme. Et quand vous trouvez une forme supérieure, vous la fermez.

+2

Malheureusement la propriété Form.TopMost obtient ou définit une valeur indiquant si le formulaire doit être affiché en tant que formulaire le plus haut. Cela ne me dit pas si le formulaire est le meilleur. – tzup

1

Vous pouvez implémenter un motif de type singleton dans votre forme la plus élevée et fournir une propriété statique qui renvoie l'instance de lui-même et la ferme simplement.

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

Je pense que vous avez mal compris la question. Imaginez une application WinForm avec une forme principale agrandie et beaucoup d'autres formes plus petites en cascade sur le formulaire principal. Chaque fois que vous appuyez sur Echap, le formulaire le plus haut doit se fermer (n'oubliez pas que le focus n'est peut-être pas actif). J'espère que cela rend les choses plus claires. – tzup

+0

Je pense que vous avez mal compris sa réponse. Il n'y a qu'un seul MainForm ouvert à la fois, n'est-ce pas? Le motif singleton présente un handle statique à la forme de n'importe où dans l'application, y compris votre crochet de clavier. –

+0

@ Zachary Yates, l'exigence est de pouvoir fermer les formulaires enfants, pas la forme principale. – tzup

3

Que diriez-vous cela en utilisant Application.Openforms

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

L'exigence consiste à fermer le formulaire le plus en haut, qui peut ne pas avoir le focus. – tzup

2

Je sais Il s'agit d'un sujet vieux de 4 ans, mais j'ai eu un problème similaire et j'ai trouvé une solution alternative au cas où quelqu'un d'autre trébucherait sur cette question et ne voulait pas déranger avec les appels Win32.

Je suppose que la forme la plus haute sera celle qui a été activée en dernier. Vous pouvez donc conserver une collection de formulaires distincte, similaire à Application.OpenForms, sauf que cette collection est triée par date de dernière activation. Chaque fois qu'un formulaire est activé, déplacez-le vers le premier élément de la collection. Chaque fois que vous voyez la touche ESC, vous fermez la collection [0] et le supprimez.

+2

Ou utilisez une pile, c'est plus naturel qu'une collection pour cela –