A pragmatic solution is to use the form's GetChildAtPoint() method, passing the 4 corners of the control. If one of them returns true then the control is definitely visible. It is not 100% reliable, all 4 corners could be overlapped by another control but still leave part of interior visible. I would not worry about that, too bizarre.
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;
}
You can invalidate the control and then call GetUpdateRect (Win32 api function) to find this out. It does have the side effect of causing a repaint, though.
Inspired by Hans's answer I've implemented this behavior in this way;
[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;
}
In order to facilitate a previous answer to your question.
Here is the source code that you will need to work with the GetUpdateRect function as jdv-Jan de Vaan answered.
[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);
}
When you need to check if a specified is visible just do something like the following:
if (IsControlVisibleToUser(controlName) == true)
{
// The Specified Control is visible.
// ... do something
}
else
{
// Control is not visible.
// ... do something else
}
Good luck.
If a control is visible the Paint event will be called (repeatedly).
Normally for not visible controls, this event will not be called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With