【发布时间】:2011-06-12 11:41:16
【问题描述】:
是否可以确定是否可以看到控件的至少一个像素(通过属性或可能使用事件通知)。
注意:我不是在寻找即使其他窗口隐藏控件也可以返回 true 的 Visible 属性
【问题讨论】:
-
不要以为内置了这样的东西,但您始终可以遍历所有顶级控件并检查它们的顶部、左侧、宽度和高度。
是否可以确定是否可以看到控件的至少一个像素(通过属性或可能使用事件通知)。
注意:我不是在寻找即使其他窗口隐藏控件也可以返回 true 的 Visible 属性
【问题讨论】:
一个实用的解决方案是使用窗体的 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;
}
【讨论】:
您可以使控件无效,然后调用GetUpdateRect(Win32 api函数)来查找。不过,它确实有导致重绘的副作用。
【讨论】:
受汉斯回答的启发,我以这种方式实现了这种行为;
[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;
}
【讨论】:
为了方便之前answer回答你的问题。
这里是使用GetUpdateRect 函数所需的源代码,jdv-Jan de Vaan 已回答。
[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 事件(重复)。
通常对于不可见的控件,该事件不会被调用。
【讨论】:
尝试了上述方法,但即使 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);
///<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;
}
}
}
【讨论】:
您可以使用控件的布局事件。 当控件进入屏幕并尝试对其子控件进行布局时触发它。
例如,假设 TabPage 中有 GroupBox。
单击相关选项卡时,将为第一个选项卡页触发布局事件,然后为 GroupBox
您可以将其与可见性属性结合使用
【讨论】:
您可以检查父控件的可见性。
protected override void OnParentVisibleChanged(EventArgs e)
{
base.OnParentVisibleChanged(e);
isVisible = true;
}
【讨论】:
我有点完成了 Hans Passant 的答案。下面的函数测试表单的所有四个角。
/// <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;
}
【讨论】: