2009-10-30 9 views
9

Normalmente si utilizza Form.Visible per verificare se Window è visibile. Ma a volte nella finestra dello schermo c'è sotto altre finestre quindi è davvero invisibile.Come verificare se la finestra è realmente visibile in Windows Form?

Quindi, come verificare in C# Windows Form se la finestra è veramente visibile o no?

Mi piacerebbe realizzare questo: quando clicco CTRL + K sulla mia tastiera e la mia finestra è visibile sullo schermo non fa nulla. Ma quando è sotto altre finestre, si apre in alto (Porta in primo piano).

cordiali saluti

risposta

1

Hm ... domanda strana. : P

Forse potresti chiedere la posizione dei moduli e, se due forme si intersecano (calcola le loro coordinate e fai un semplice metodo) controlla se una forma ha Focus(). Se è attivo, l'altro deve essere "invisibile" (nel senso che un utente non può vederlo perché è sotto l'altro modulo).

Ovviamente questo metodo è hacky al meglio, ma è qualcosa con cui si può iniziare a lavorare.

2

Per rispondere alla domanda come richiesto, è possibile provare a chiamare la funzione API WindowFromPoint per trovare la finestra in vari punti del modulo e verificare se restituisce l'handle di ciò che si prevede di essere in quel punto.

+0

Questa è la migliore risposta per me dopo aver setacciato il web e SO. SLaks per il salvataggio! Quella credibilità SO parla da sola. –

4

È possibile utilizzare l'API di Windows per enumerare tutte le finestre, recuperare il loro ordine Z e confrontarlo con l'ordine Z della finestra. Penso che qualcuno abbia già fatto questo here.

0

Dovresti essere in grado di scoprire se la tua finestra è visibile sovrascrivendo il metodo OnPaint. Dovrai passare il controllo alla classe base per eseguire la verniciatura effettiva, ma sarai in grado di rilevare se viene ricevuto un messaggio di disegno. Aggiornamento: no, questo non funziona, scusa!

In linea di principio, il metodo Activate dovrebbe portare la finestra in primo piano, ma in pratica ho sempre riscontrato questo problema se altri processi hanno il focus di input. Se vuoi davvero che qualcuno veda una finestra, imposta il bit più in alto, ma aspettati che siano infastiditi! Un modo sicuro per attirare l'attenzione su una finestra è chiuderlo e riaprirlo, se riesci a farla franca.

Un modo per ottenere ciò che stai cercando è utilizzare un'icona di notifica, che attirerà l'attenzione dell'utente in un modo conforme alle linee guida dell'interfaccia utente di Windows.

6

Ho cercato su Google web, ma non ho trovato alcuna risposta diretta per vedere se una parte di una finestra è veramente visibile all'utente. In realtà avevo bisogno di un modo per "battere" il modulo, se il mouse è attualmente in cima alla parte visibile della finestra. Ho pensato di condividere il codice che è durato diversi giorni:

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

Si potrebbe anche ..:) ottiene la proprietà ClickablePoint da AutomationElement corrispondente alla finestra. Non sono sicuro al 100% se questo è completamente accurato però .. ha funzionato nel 99% dei casi per me e sto ancora controllando l'altro 1%, dove il problema sta (potrebbe essere dalla mia parte o cattiva gestione utenti, o.)

-3

Impostare semplicemente la proprietà Form.AlwaysOnTop su true.

+0

Non aiuta se un'altra finestra ha la stessa proprietà – Jules

1

Ho provato a implementare il suggerimento di SLaks. Anche se l'ho scritto in VB.NET, non 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 
Problemi correlati