2009-10-30 11 views
9

Normalmente utiliza Form.Visible para comprobar si la ventana es visible en absoluto. Pero a veces en la ventana de la pantalla está debajo de otras ventanas, así que es realmente invisible.¿Cómo comprobar si la ventana es realmente visible en Windows Forms?

Entonces, ¿cómo comprobar en C# Windows Forms si la ventana está realmente visible o no?

Me gustaría lograr esto: cuando hago clic en CTRL + K en mi teclado y mi ventana está visible en mi pantalla, no hace nada. Pero cuando está debajo de otras ventanas, aparece en la parte superior (Traer al frente).

saludos cordiales

Respuesta

1

Hm ... pregunta extraña. : P

Quizás podría preguntar la ubicación de los formularios, y si dos formularios se interponen (descubra sus coordenadas, y haga un método simple) verifique si un formulario tiene el Enfoque(). Si tiene foco, entonces el otro debe ser "invisible" (en el sentido de que un usuario no puede verlo porque está debajo del otro formulario).

Obviamente, este método es hacky en el mejor, pero es algo con lo que puedes empezar a trabajar.

2

Para responder la pregunta como se le solicitó, puede intentar llamar a la función de API WindowFromPoint para encontrar la ventana en varios puntos de su formulario y comprobar si devuelve el identificador de lo que espera que sea en ese momento.

+0

Esta es la mejor respuesta para mí después de recorrer la web y SO. SLaks al rescate! Esa credibilidad SO habla por sí misma. –

4

Puede usar la API de Windows para enumerar todas las ventanas, recuperar su orden Z y compararla con el orden Z de su ventana. Creo que alguien ya hizo esto here.

0

Debería poder averiguar si su ventana está visible anulando el método OnPaint. Querrá pasar el control a la clase base para realizar la pintura real, pero podrá detectar si se recibe un mensaje de pintura. Actualización: no, esto no funciona, ¡Lo siento!

En principio, el método Activate debe llevar su ventana al primer plano, pero en la práctica siempre me ha resultado problemática si otros procesos tienen el foco de entrada. Si realmente quieres que alguien vea una ventana, configura el bit más alto, ¡pero espera que se molesten! Una manera infalible de llamar la atención por una ventana es cerrarla y volver a abrirla, si puedes salirte con la tuya.

Una forma de lograr lo que está buscando es utilizar un ícono de notificación, esto atraerá la atención del usuario de una manera que cumpla con las pautas de la interfaz de usuario de Windows.

6

Busqué en la web, pero no encontré ninguna respuesta directa para ver si una parte de una ventana es realmente visible para el usuario. De hecho, necesitaba una forma de "probar" el formulario, si el mouse está actualmente en la parte visible de la ventana. Pensé que compartiría el código que tomó varios días para lograr:

public class VisibilityTester 
{ 
    private delegate bool CallBackPtr(int hwnd, int lParam); 
    private static CallBackPtr callBackPtr; 

    /// <summary> 
    /// The enumerated pointers of actually visible windows 
    /// </summary> 
    public static List<IntPtr> enumedwindowPtrs = new List<IntPtr>(); 
    /// <summary> 
    /// The enumerated rectangles of actually visible windows 
    /// </summary> 
    public static List<Rectangle> enumedwindowRects = new List<Rectangle>(); 

    /// <summary> 
    /// Does a hit test for specified control (is point of control visible to user) 
    /// </summary> 
    /// <param name="ctrlRect">the rectangle (usually Bounds) of the control</param> 
    /// <param name="ctrlHandle">the handle for the control</param> 
    /// <param name="p">the point to test (usually MousePosition)</param> 
    /// <param name="ExcludeWindow">a control or window to exclude from hit test (means point is visible through this window)</param> 
    /// <returns>boolean value indicating if p is visible for ctrlRect</returns> 
    public static bool HitTest(Rectangle ctrlRect, IntPtr ctrlHandle, Point p, IntPtr ExcludeWindow) 
    { 
     // clear results 
     enumedwindowPtrs.Clear(); 
     enumedwindowRects.Clear(); 

     // Create callback and start enumeration 
     callBackPtr = new CallBackPtr(EnumCallBack); 
     EnumDesktopWindows(IntPtr.Zero, callBackPtr, 0); 

     // Go from last to first window, and substract them from the ctrlRect area 
     Region r = new Region(ctrlRect); 

     bool StartClipping = false; 
     for (int i = enumedwindowRects.Count - 1; i >= 0; i--) 
     { 
      if (StartClipping && enumedwindowPtrs[i] != ExcludeWindow) 
      { 
       r.Exclude(enumedwindowRects[i]); 
      } 

      if (enumedwindowPtrs[i] == ctrlHandle) StartClipping = true; 
     } 

     // return boolean indicating if point is visible to clipped (truly visible) window 
     return r.IsVisible(p); 
    } 

    /// <summary> 
    /// Window enumeration callback 
    /// </summary> 
    private static bool EnumCallBack(int hwnd, int lParam) 
    { 
     // If window is visible and not minimized (isiconic) 
     if (IsWindow((IntPtr)hwnd) && IsWindowVisible((IntPtr)hwnd) && !IsIconic((IntPtr)hwnd)) 
     { 
      // add the handle and windowrect to "found windows" collection 
      enumedwindowPtrs.Add((IntPtr)hwnd); 

      RECT rct; 

      if (GetWindowRect((IntPtr)hwnd, out rct)) 
      { 
       // add rect to list 
       enumedwindowRects.Add(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); 
      } 
      else 
      { 
       // invalid, make empty rectangle 
       enumedwindowRects.Add(new Rectangle(0, 0, 0, 0)); 
      } 
     } 

     return true; 
    } 


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

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

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

    [DllImport("user32.dll")] 
    private static extern int EnumDesktopWindows(IntPtr hDesktop, CallBackPtr callPtr, int lPar); 

    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 

    [StructLayout(LayoutKind.Sequential)] 
    private struct RECT 
    { 
     public int Left;  // x position of upper-left corner 
     public int Top;   // y position of upper-left corner 
     public int Right;  // x position of lower-right corner 
     public int Bottom;  // y position of lower-right corner 

     public override string ToString() 
     { 
      return Left + "," + Top + "," + Right + "," + Bottom; 
     } 
    } 
} 
1

También podría ..:) obtener la propiedad ClickablePoint de AutomationElement correspondiente a la ventana. No estoy 100% seguro de si esto es completamente exacto. Ha funcionado en el 99% de los casos para mí y todavía estoy revisando el otro 1%, donde el problema radica (podría estar de mi lado o mal manejo del usuario, o.)

-3

Simplemente configure la propiedad Form.AlwaysOnTop en true.

+0

No ayudará si otra ventana también tiene la misma propiedad – Jules

1

Al principio intenté implementar la sugerencia de SLaks. Aunque lo escribí en VB.NET, no en C#

Friend Structure PointStruct 
    Public x As Int32 
    Public y As Int32 
End Structure 

<System.Runtime.InteropServices.DllImport("user32.dll")> _ 
Friend Function WindowFromPoint(ByVal Point As PointStruct) As IntPtr 
End Function 

''' <summary> 
''' Checks if a control is actually visible to the user completely 
''' </summary> 
''' <param name="control">The control to check.</param> 
''' <returns>True, if the control is completely visible, false else.</returns> 
''' <remarks>This is not 100% accurate, but feasible enough for my purpose.</remarks> 
Public Function IsControlVisibleToUser(ByVal control As Windows.Forms.Control) As Boolean 
    If Not control.Visible Then Return False 

    Dim bAllPointsVisible As Boolean = True 
    Dim lPointsToCheck As New List(Of Point) 
    'Add the points to check. In this case add the edges and some border points 
    'between the edges. 
    'Strangely, the exact edge points always return the false handle. 
    'So we add a pixel into the control. 
    lPointsToCheck.Add(New Point(control.Left + 1, control.Top + 1)) 
    lPointsToCheck.Add(New Point(control.Right - 1, control.Top + 1)) 
    lPointsToCheck.Add(New Point(control.Right - 1, control.Bottom - 1)) 
    lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - 1)) 
    lPointsToCheck.Add(New Point(control.Left + control.Width/2, control.Top + 1)) 
    lPointsToCheck.Add(New Point(control.Right - 1, control.Top + control.Height/2)) 
    lPointsToCheck.Add(New Point(control.Right - control.Width/2, control.Bottom - 1)) 
    lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - control.Height/2)) 
    'lPointsToCheck.Add(New Point(control.Left + control.Width/2, control.Top + control.Height/2)) 

    'Check each point. If all points return the handle of the control, 
    'the control should be visible to the user. 
    For Each oPoint In lPointsToCheck 
     Dim sPoint As New PointStruct() With { 
      .x = oPoint.X, _ 
      .y = oPoint.Y _ 
     } 
     bAllPointsVisible = bAllPointsVisible And (_ 
      (WindowFromPoint(sPoint) = control.Handle) _ 
     ) 
    Next 

    Return bAllPointsVisible 
End Function 
Cuestiones relacionadas