Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#winform检查控件是否物理可见_C#_Winforms_Controls - Fatal编程技术网

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

C#winform检查控件是否物理可见,c#,winforms,controls,C#,Winforms,Controls,是否可以确定是否至少可以看到控件的一个像素(通过属性或使用事件通知) 注意:我不寻找Visible属性,该属性可以返回true,即使其他窗口隐藏控件如果控件可见,则将(重复)调用绘制事件 通常,对于不可见的控件,不会调用此事件。您可以使该控件无效,然后调用(Win32 api函数)来查找此问题。不过,它确实有导致重新喷漆的副作用 一个实用的解决方案是使用表单的GetChildAtPoint()方法,传递控件的4个角。如果其中一个返回true,则控件肯定可见。它不是100%可靠,所有4个角都可以被

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


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

如果控件可见,则将(重复)调用绘制事件


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

您可以使该控件无效,然后调用(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;
}

为了便于对您的问题进行进一步的讨论

下面是您使用所回答的函数所需的源代码

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

祝你好运。

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

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

您可以使用事件控件的布局。 当控件出现在屏幕上并尝试布局其子控件时,会触发该控件

例如,假设TabPage中有一个GroupBox。
单击相关选项卡时,将为第一个tabpage触发布局事件,然后为GroupBox触发布局事件

您可以将其与可见性属性结合使用

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

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

我差不多完成了汉斯·帕桑的回答。下面的函数测试窗体的所有四个角

        /// <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;
    }
//
///确定窗体是否位于顶部且确实可见。
///遇到的一个问题是form.invalidate返回true,即使另一个表单位于它的顶部。
///此函数可避免这种情况
///代码和讨论:
/// https://stackoverflow.com/questions/4747935/c-sharp-winform-check-if-control-is-physicaly-visible
/// 
/// 
/// 
public bool ChildReallyVisible(控制子对象)
{
布尔结果=假;
var pos=this.PointToClient(child.PointToScreen(Point.Empty));
结果=此。GetChildAtPoint(pos)=子对象;
//这“如果”会导致仅在结果为真时检查条件,否则将一直保持为假
如果(结果)
{
结果=(this.GetChildAtPoint(新点(pos.X+child.Width-1,pos.Y))==child);
}
如果(结果)
{
结果=(this.GetChildAtPoint(新点(位置X,位置Y+child.Height-1))==child;
}
如果(结果)
{
结果=(this.GetChildAtPoint(新点(位置X+child.Width-1,位置Y+child.Height-1))==child);
}
返回结果;
}

尝试了上述方法,但即使winform被另一个应用程序覆盖,也会继续实现

最终使用了以下内容(在我的winform类中):

使用系统;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
名称空间yourNameSpace
{
公共类Myform:Form
{
private void SomeFuncedByTimeRonMainThread()调用的函数
{
bool isVisible=isControlVisible(此);
//做点什么。
}
[DllImport(“user32.dll”)]
静态外部IntPtr WindowFromPoint(System.Drawing.Point p);
///------------------------------------------------------------------------------------
///
///如果控件在屏幕上可见,则返回true,否则返回false。
///
///------------------------------------------------------------------------------------
专用bool isControlVisible(控制)
{
布尔结果=假;
if(控件!=null)
{
var pos=控制点到屏幕(系统.绘图.点.空);
变量句柄=WindowFromPoint(新系统绘图点(位置X+10,位置Y+10));//+10忽略填充
result=(control.Handle==Handle);//如果控件可见,则应等于
}
返回结果;
}
}
}

不要认为内置了这样的东西,但您可以始终迭代所有顶级控件,并检查它们的顶部、左侧、宽度和高度。我想说的是简单而聪明的解决方案。好吧。但是我应该在疼痛事件上测试什么呢?(DateTime.now-lastpaindt<10秒)?这不是很干净…@Toto:取决于你需要什么。触发可见性的开始很容易,而触发可见性的结束却不那么容易。我建议将“this”替换为“child.parent”,这将使代码更易于移植和维护。另外,你可以把函数设为静态的,我相信这是非常有用的。哦,顺便说一下,你的代码运行得很好。谢谢。一点也不,那会毁了密码的。这必须是它工作的表单。非常有趣,我已经使用了子控件的父控件,即使子控件的父控件不是它仍然工作的表单。然而,我目前使用的是jdv Jan de Vaan建议的方法,我认为这更可靠。确实是一个务实的解决方案。只是有一个例子,一些控件根据用户的操作隐藏了其他控件,并且必须找出哪些控件实际上是可见的。此解决方案避免手动复制Windows窗体完成的作业。为了实现我只保留这些控件的列表,请附加一个VisibleChanged事件,该事件在所有控件上运行Hans的代码,使用此重载跳过不可见的控件:。非常好的解决方案,先生!我正在开发一个winforms应用程序,它将在没有键盘的触摸屏上运行,我以重叠模式显示不同的表单。我只希望实际位于顶部(因此可见)的表单具有事件
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);


        ///<summary><para>------------------------------------------------------------------------------------</para>
        ///
        ///<para>           Returns true if the control is visible on screen, false otherwise.                </para>
        ///
        ///<para>------------------------------------------------------------------------------------</para></summary>
        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;
        }
    }
}