C#winform检查控件是否在物理上可见

是否可以确定是否可以看到控件的至少一个像素(通过属性或可能使用事件通知)。

注意:即使其他窗口隐藏了控件,我也不会寻找可以返回true的Visible属性

您可以使控件无效,然后调用GetUpdateRect (Win32 api函数)来查找它。 但它确实会产生重画的副作用。

一个实用的解决方案是使用窗体的GetChildAtPoint()方法,传递控件的4个角。 如果其中一个返回true,则控件肯定可见。 它不是100%可靠,所有4个角都可以被另一个控件重叠,但仍然可以看到内部的一部分。 我不担心,太奇怪了。

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

为了方便以前回答您的问题。

以下是jdv-Jan de Vaan回答的使用GetUpdateRect函数所需的源代码。

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

当您需要检查指定是否可见时,只需执行以下操作:

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

祝好运。

如果控件可见,将重复调用Paint事件。

通常,对于不可见的控件,不会调用此事件。

受Hans的回答启发,我以这种方式实现了这种行为;

  [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(pX, pY); } public static implicit operator POINT(System.Drawing.Point p) { return new POINT(pX, pY); } } 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; } 

试过上述但即使winform被另一个应用程序覆盖也一直都是真的。

结束使用以下(在我的winform类中):

 using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace yourNameSpace { public class Myform : Form { private void someFuncInvokedByTimerOnMainThread() { bool isVisible = isControlVisible(this); // do something. } [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(System.Drawing.Point p); ///------------------------------------------------------------------------------------ /// /// Returns true if the control is visible on screen, false otherwise.  /// ///------------------------------------------------------------------------------------ private bool isControlVisible(Control control) { bool result = false; if (control != null) { var pos = control.PointToScreen(System.Drawing.Point.Empty); var handle = WindowFromPoint(new System.Drawing.Point(pos.X + 10, pos.Y + 10)); // +10 to disregard padding result = (control.Handle == handle); // should be equal if control is visible } return result; } } } 

您可以使用控件的布局事件。 当控件进入屏幕并尝试布局其子控件时触发它。

例如,假设TabPage中有GroupBox。
单击相关选项卡后,布局事件将触发第一个tabpage,然后触发GroupBox
您可以将它与可见性属性结合使用

您可以检查父控件的可见性。

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

我有点完成了Hans Passant的回答。 下面的函数测试表单的所有四个角。

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