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

Tot*_*oto 28 c# controls winforms

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

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

Han*_*ant 15

一个实用的解决方案是使用窗体的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;
}
Run Code Online (Sandbox Code Playgroud)

  • 完全没有,这会破坏代码.***必须是它的工作形式. (2认同)

小智 7

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


Seb*_*Piu 7

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

    [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;
    }
Run Code Online (Sandbox Code Playgroud)


Fáb*_*nes 5

为了便于您以前回答您的问题.

以下是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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

祝好运.