2011-01-20 20 views
24

¿Es posible determinar si se puede ver al menos un píxel de control (por una propiedad o tal vez mediante la notificación de eventos).C# winform check si el control es físicamente visible

NB: No estoy buscando la propiedad Visible que puede devolver un cierto incluso si otra ventana oculta el control de

+0

No creo que haya tal cosa incorporada, pero siempre se puede iterar sobre todos los controles de nivel superior y comprobar su superior, izquierda, anchura y altura. –

Respuesta

6

Puede invalidar el control y luego llamar al GetUpdateRect (función API de Win32) para averiguarlo. Sin embargo, tiene el efecto secundario de causar un repintado.

+1

Probado esto, devuelve verdadero (visible) cuando se superpone con otros controles/ventanas, devuelve falso solo cuando se minimiza el control/ventana. ¿Estás seguro de que este enfoque funciona? – Nemo

+1

Ese es un caso difícil. Windows aún dibujará el control, para garantizar que pueda combinar los controles alfa. Funcionará si hace invisible el control primario o desplaza el control fuera del rectángulo visible de sus padres, que son casos de uso importantes. –

+0

Gracias por la respuesta, pero Alpha blend no debería ocurrir ya que la ventana que se superpone al control tiene transparencia nula y también cubre completamente el control, no entiendo por qué GetUpdateRect() aún devuelve verdadero. – Nemo

3

Si un control es visible el evento Paint se llamará (varias veces).

Normalmente, para controles no visibles, no se llamará a este evento.

+0

Diría que es una solución fácil e inteligente. –

+2

Ok ... pero ¿qué debo probar en el evento Pain? (DateTime.now-lastPaintDt <10 secondes)? Esto no es muy limpio ... – Toto

+0

@Toto: Depende de lo que necesites. Es fácil activar el inicio de la visibilidad, no tan fácil de desencadenar el final. – GvS

14

Una solución pragmática es utilizar el método GetChildAtPoint() del formulario, pasando las 4 esquinas del control. Si uno de ellos devuelve verdadero, entonces el control es definitivamente visible. No es 100% confiable, las 4 esquinas pueden estar superpuestas por otro control pero aún así dejan parte del interior visible. No me preocuparía por eso, demasiado extraño.

public bool ChildReallyVisible(Control child) { 
    var pos = this.PointToClient(child.PointToScreen(Point.Empty)); 

    //Test the top left 
    if (this.GetChildAtPoint(pos) == child) return true; 

    //Test the top right 
    if (this.GetChildAtPoint(new Point(pos.X + child.Width - 1, pos.Y)) == child) return true; 

    //Test the bottom left 
    if (this.GetChildAtPoint(new Point(pos.X, pos.Y + child.Height -1)) == child) return true; 

    //Test the bottom right 
    if (this.GetChildAtPoint(new Point(pos.X + child.Width - 1, pos.Y + child.Height -1)) == child) return true; 

    return false; 
} 
+1

Sugiero reemplazar "esto" con "child.parent", haría que el código sea más portátil y más fácil de mantener. Además, entonces puedes hacer que la función sea estática, lo cual es bastante útil, creo. Ah, por cierto, tu código funciona bastante bien. Gracias. –

+2

En absoluto, eso arruinaría el código. * this * debe ser la forma para que funcione. –

+0

Lo suficientemente interesante, lo he usado con el control principal del hijo, e incluso cuando el padre del hijo no es el formulario que todavía funcionaba. Sin embargo, actualmente estoy usando el modo sugerido por jdv-Jan de Vaan, que creo que es más confiable. –

4

Para facilitar una pregunta anterior answer a su pregunta.

Aquí está el código fuente que necesitará para trabajar con la función GetUpdateRect como jdv-Jan de Vaan respondida.

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
internal struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 
    public int Width { get { return this.Right - this.Left; } } 
    public int Height { get { return this.Bottom - this.Top; } } 
} 
[System.Runtime.InteropServices.DllImport("user32.dll")] 
internal static extern bool GetUpdateRect(IntPtr hWnd, ref RECT rect, bool bErase); 
public static bool IsControlVisibleToUser(Control control) 
{ 
    control.Invalidate(); 
    Rectangle bounds = control.Bounds; 
    RECT rect = new RECT { Left = bounds.Left, Right = bounds.Right, Top = bounds.Top, Bottom = bounds.Bottom }; 
    return GetUpdateRect(control.Handle, ref rect, false); 
} 

Cuando es necesario comprobar si un determinado es visible justo hacer algo como lo siguiente:

if (IsControlVisibleToUser(controlName) == true) 
{ 
    // The Specified Control is visible. 
    // ... do something 
} 
else 
{ 
    // Control is not visible. 
    // ... do something else 
} 

Buena suerte.

3

Inspirado por la respuesta de Hans, he implementado este comportamiento de esta manera;

[DllImport("user32.dll")] 
    static extern IntPtr WindowFromPoint(POINT Point); 

    [StructLayout(LayoutKind.Sequential)] 
    public struct POINT 
    { 
     public int X; 
     public int Y; 

     public POINT(int x, int y) 
     { 
      this.X = x; 
      this.Y = y; 
     } 

     public static implicit operator System.Drawing.Point(POINT p) 
     { 
      return new System.Drawing.Point(p.X, p.Y); 
     } 

     public static implicit operator POINT(System.Drawing.Point p) 
     { 
      return new POINT(p.X, p.Y); 
     } 
    } 

    public static bool IsControlVisibleToUser(this Control control) 
    { 
     var pos = control.PointToScreen(control.Location); 
     var pointsToCheck = new POINT[] 
           { 
            pos, 
            new Point(pos.X + control.Width - 1, pos.Y), 
            new Point(pos.X, pos.Y + control.Height - 1), 
            new Point(pos.X + control.Width - 1, pos.Y + control.Height - 1), 
            new Point(pos.X + control.Width/2, pos.Y + control.Height/2) 
           }; 

     foreach (var p in pointsToCheck) 
     { 
      var hwnd = WindowFromPoint(p); 
      var other = Control.FromChildHandle(hwnd); 
      if (other == null) 
       continue; 

      if (control == other || control.Contains(other)) 
       return true; 
     } 

     return false; 
    } 
0

Puede usar el evento de controles Layout. se activa cuando el control llega a la pantalla e intenta diseñar sus controles secundarios.

Por ejemplo, digamos que hay GroupBox dentro de una TabPage.
Cuando hace clic en la pestaña correspondiente, evento layout se activará para primera TabPage a continuación para GroupBox
Es posible utilizarlo combinado con propiedad de visibilidad

0

Puede comprobar la visibilidad de control primario.

protected override void OnParentVisibleChanged(EventArgs e) 
    { 
     base.OnParentVisibleChanged(e); 
     isVisible = true; 
    } 
0

I algo terminó la respuesta de Hans Passant. La función siguiente prueba las cuatro esquinas del formulario.

 /// <summary> 
    /// determines if a form is on top and really visible. 
    /// a problem you ran into is that form.invalidate returns true, even if another form is on top of it. 
    /// this function avoids that situation 
    /// code and discussion: 
    /// https://stackoverflow.com/questions/4747935/c-sharp-winform-check-if-control-is-physicaly-visible 
    /// </summary> 
    /// <param name="child"></param> 
    /// <returns></returns> 
    public bool ChildReallyVisible(Control child) 
    { 
     bool result = false; 

     var pos = this.PointToClient(child.PointToScreen(Point.Empty)); 

     result = this.GetChildAtPoint(pos) == child; 
     //this 'if's cause the condition only to be checked if the result is true, otherwise it will stay false to the end 
     if(result) 
     { 
      result = (this.GetChildAtPoint(new Point(pos.X + child.Width - 1, pos.Y)) == child); 
     } 
     if(result) 
     { 
      result = (this.GetChildAtPoint(new Point(pos.X, pos.Y + child.Height - 1)) == child); 
     } 
     if(result) 
     { 
      result = (this.GetChildAtPoint(new Point(pos.X + child.Width - 1, pos.Y + child.Height - 1)) == child) ; 
     } 
     return result; 
    }