Pregunta

Normalmente usa Form.Visible para verificar si Window es visible en absoluto. Pero a veces en la ventana de la pantalla está debajo de otras ventanas, por lo que es realmente invisible.

Entonces, ¿cómo verificar en C # Windows Forms si la ventana es 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

¿Fue útil?

Solución

Puede llamar al Activate en el formulario para traerlo al frente si aún no lo está.

Sin embargo, tenga en cuenta que si un programa diferente está activo, generalmente simplemente parpadeará el botón del escritorio (dependiendo de dónde lo llame). Esta es la protección estándar de Windows contra el robo de foco y no debe intentar solucionarlo .

Otros consejos

Busqué en Google a través de la web, pero no pude encontrar ninguna respuesta directa para ver si una parte de una ventana es realmente visible para el usuario. En realidad necesitaba una forma de "hittest" el formulario, si el mouse está actualmente en la parte superior de la parte visible de la ventana. Pensé en compartir el código, que tardó varios días en realizarse:

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;
        }
    }
}

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

Para responder la pregunta tal como se le preguntó, puede intentar llamar al WindowFromPoint Función API para encontrar la ventana en varios puntos de su formulario y verificar si devuelve el identificador de lo que espera que sea en ese punto.

Hm ... pregunta rara. : P

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

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

También podría ... :) obtener la propiedad ClickablePoint del AutomationElement correspondiente a la ventana. Sin embargo, no estoy 100% seguro de si esto es completamente exacto ... me ha funcionado en el 99% de los casos y todavía estoy comprobando en el otro 1%, dónde radica el problema (podría ser de mi parte o mal usuario manejo, o.)

Realmente intenté implementar la sugerencia de SLack. Aunque lo escribí en VB.NET, no 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

Debería poder averiguar si su ventana es visible anulando el método OnPaint. Deberá 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 debería poner su ventana en primer plano, pero en la práctica siempre he encontrado esto problemático si otros procesos tienen el foco de entrada. Si realmente quieres que alguien vea una ventana, establece el bit superior, ¡pero espera que se moleste! Una forma segura de llamar la atención sobre una ventana es cerrarla y volver a abrirla, si puede salirse con la suya.

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

Simplemente configure la propiedad Form.AlwaysOnTop en true .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top