How can I check if a form is behind another window? - winforms

Is there a property for a form that I can check to know when it is behind another window?
I have an OpenGL based virtual environment running inside a form.
I would like to be able to ignore the mouse when clicking on another window that is on top of this form.
I looked at this thread but the title of the question does not match the answer.

Hans is, as always, right. Pinvoke is your friend here.. I found a solution to my needs in the msdn forum, this method uses a number of WinAPI-calls to determine if a window is overlapped. I did some minor modifications to enable ignoring certain handles. The method returns true as long as another window is overlapping it slightly, you may need to make your own modifications..
private const int GW_HWNDPREV = 3;
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("user32.dll")]
private static extern bool IntersectRect(out RECT lprcDst, [In] ref RECT lprcSrc1, [In] ref RECT lprcSrc2);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
public static bool IsWindowOverlapped(IntPtr hWnd, HashSet<IntPtr> ignoreHandles)
{
if (hWnd == IntPtr.Zero)
return false; //throw new InvalidOperationException("Window does not yet exist");
if (!IsWindowVisible(hWnd))
return false;
HashSet<IntPtr> visited = new HashSet<IntPtr> { hWnd };
//The set is used to make calling GetWindow in a loop stable by checking if we have already
//visited the window returned by GetWindow. This avoids the possibility of an infinate loop.
RECT thisRect = new RECT();
GetWindowRect(hWnd, ref thisRect);
while ((hWnd = GetWindow(hWnd, GW_HWNDPREV)) != IntPtr.Zero && !visited.Contains(hWnd))
{
visited.Add(hWnd);
if (ignoreHandles != null && ignoreHandles.Contains(hWnd))
continue;
RECT testRect, intersection;
testRect = intersection = new RECT();
if (IsWindowVisible(hWnd) && GetWindowRect(hWnd, ref testRect) && IntersectRect(out intersection, ref thisRect, ref testRect))
return true;
}
return false;
}

Related

Determine WPF window location such that it is visible within screen bounds all the time (like OS's right click context menu)

As you can see in the image, I want to open a fixed sized WPF window at a location of Launch window (which is WinForms app). How do I make sure that the WPF windows about to open is placed such that it is fully visible on any side of that Launch window. The similar behaviour is there in Windows desktop's right click menu as if you click on extreme edge of the screen, it would open context menu at the left and if you are in middle of the screen, it would open either side.
I have tried few things already and also this SO answer also but still figuring out how to calculate Windows's bounds such that it is within visible area.
WPF determine element is visible on screen
The process would be:
Get the rectangle of the window using its Handle.
Get the handle of the monitor where the window locates.
Get the information (in particular, rectangle of working area) of the monitor using its Handle.
Calculate the direction of available space.
using System;
using System.Runtime.InteropServices;
public enum Direction { None, TopLeft, TopRight, BottomRight, BottomLeft }
public static class WindowHelper
{
public static Direction GetAvailableDirection(IntPtr windowHandle)
{
if (!GetWindowRect(windowHandle, out RECT buffer))
return Direction.None;
System.Drawing.Rectangle windowRect = buffer;
IntPtr monitorHandle = MonitorFromWindow(windowHandle, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL);
if (monitorHandle == IntPtr.Zero)
return Direction.None;
MONITORINFO info = new() { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (!GetMonitorInfo(monitorHandle, ref info))
return Direction.None;
System.Drawing.Rectangle workingAreaRect = info.rcWork;
bool isWindowAlignedTop = (windowRect.Top - workingAreaRect.Top) < (workingAreaRect.Bottom - windowRect.Bottom);
bool isWindowAlignedLeft = (windowRect.Left - workingAreaRect.Left) < (workingAreaRect.Right - windowRect.Right);
return (isWindowAlignedTop, isWindowAlignedLeft) switch
{
(true, true) => Direction.BottomRight,
(true, false) => Direction.BottomLeft,
(false, true) => Direction.TopRight,
(false, false) => Direction.TopLeft
};
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(
IntPtr hWnd,
out RECT lpRect);
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromWindow(
IntPtr hwnd,
MONITOR_DEFAULTTO dwFlags);
private enum MONITOR_DEFAULTTO : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public static implicit operator System.Drawing.Rectangle(RECT rect)
{
return new System.Drawing.Rectangle(
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top);
}
}
}
Please note that calling app must have an application manifest which includes DPI awareness for correct calculation.

How can I know whether a form is showing to user?

I have a system monitor program which cost system resource heavly. I hope the program run tasks only when the form is showing to users. One way is to check whether the form is foreground window or active window.
But if the form is not activated but is showing to users (for example, you can see two windows in following picture, one of the window is not activated but showing to users), I can't handle the situation well.
window example http://www.chip.de/ii/117599040_a933fb45fe.gif
Any ideas? Thanks in advanced.
Edit:
I have found the solution according to my colleague's suggest.
Here is my solution:
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool IsZoomed(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool IsWindowVisible(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "GetWindow", SetLastError = true)]
static extern IntPtr GetNextWindow(IntPtr hWnd, uint cmd);
[DllImport("user32.dll", EntryPoint = "GetWindowText", SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
private static extern IntPtr GetForegroundWindow();
//I have made some simplifications. My form is maximized. If the form is not
// maximized, you should calculate the region of current form and top level form
// to check it.
private bool IsShowing()
{
if(this.Visible == false || this.WindowState = FormWindowState.Minimized)
return false;
IntPtr currentHandler = this.Handle;
if (currentHandler == GetForegroundWindow())
return true;
while (currentHandler != GetForegroundWindow())
{
IntPtr hwnd = GetNextWindow(currentHandler, (uint)GetWindow_Cmd.GW_HWNDPREV);
currentHandler = hwnd;
if (IsZoomed(currentHandler) && IsWindowVisible(currentHandler))
{
StringBuilder sb = new StringBuilder(255);
GetWindowText(currentHandler, sb, sb.Capacity + 1);
Debug.WriteLine("masked by:" + sb.ToString());
return false;
}
}
if (IsZoomed(currentHandler) && IsWindowVisible(currentHandler))
{
StringBuilder sb = new StringBuilder(255);
GetWindowText(currentHandler, sb, sb.Capacity + 1);
Debug.WriteLine("masked by:" + sb.ToString());
return false;
}
return true;
}
It is not clear when you want to run or not your tasks, but in any case, I think that the best approach is to let the system notify you when your form acvtive or visible state changes. In this case you should write the activation and deactivation tasks's code in the forms event Activated, Deactivate and/or VisibleChanged
From MSDN
Deactivate: Occurs when the form loses focus and is no longer the
active form.
Activated: Occurs when the form is activated in code or by the user.
VisibleChanged: Occurs when the Visible property value changes.
You could also find useful to refer to this article that shows the order of events in the loading and closing of a form

Is there a way of taking a screenshot of the whole screen with White?

Is there an API that would let me do it in http://teststack.github.com/White/?
I can't seem to find it.
Thanks
Pawel
I know this is a very old post. But I though can't hurt to update it. TestStack.White has these functions now:
//Takes a screenshot of the entire desktop, and saves it to disk
Desktop.TakeScreenshot("C:\\white-framework.png", System.Drawing.Imaging.ImageFormat.Png);
//Captures a screenshot of the entire desktop, and returns the bitmap
Bitmap bitmap = Desktop.CaptureScreenshot();
From looking through the code on GitHub, it doesn't appear to have an API for that (perhaps add it as a feature request?).
You can do it fairly simply yourself though using a combination of the Screen class and Graphics.CopyFromScreen. There are some examples of how to capture the screen or the active window in the answers to this question.
The White.Repository project actually records the flow of your test, with screenshots but it is not very well documented and is not released on NuGet yet (it will be soon).
Personally I use this class we have put together from a bunch of sources and forget where I got it originally. This captures modal dialogues and other things that many of the other implementations didn't capture for some reason.
/// <summary>
/// Provides functions to capture the entire screen, or a particular window, and save it to a file.
/// </summary>
public class ScreenCapture
{
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
public Bitmap CaptureScreenShot()
{
var sz = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size;
var hDesk = GetDesktopWindow();
var hSrce = GetWindowDC(hDesk);
var hDest = CreateCompatibleDC(hSrce);
var hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
var hOldBmp = SelectObject(hDest, hBmp);
BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
var bmp = Image.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
return bmp;
}
}
Then to consume
var sc = new ScreenCapture();
var bitmap = sc.CaptureScreenShot();
bitmap.Save(fileName + ".png"), ImageFormat.Png);

Open DateTimePicker C# control programmatically

How can I open the DateTimePicker C# control programmatically?
I want to show the Calendar in the Datetime Picker control by sending keys to the control.
Is there a way we can do that?
Try the following
//part of the usings
using System.Runtime.InteropServices;
//declares
[DllImport("user32.dll")]
private static extern bool PostMessage(
IntPtr hWnd, // handle to destination window
Int32 msg, // message
Int32 wParam, // first message parameter
Int32 lParam // second message parameter
);
const Int32 WM_LBUTTONDOWN = 0x0201;
//method to call dropdown
private void button1_Click(object sender, EventArgs e)
{
Int32 x = dateTimePicker1.Width - 10;
Int32 y = dateTimePicker1.Height / 2;
Int32 lParam = x + y * 0x00010000;
PostMessage(dateTimePicker1.Handle, WM_LBUTTONDOWN, 1,lParam);
}
On my system (Windows 7, .NET 35) the other solutions did not work. I found another solution on a MS discussion site that did work.
using System.Runtime.InteropServices;
public static class Extensions
{
[DllImport("user32.dll", SetLastError = true)]
private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
private const uint WM_SYSKEYDOWN = 0x104;
public static void Open(this DateTimePicker obj)
{
SendMessage(obj.Handle, WM_SYSKEYDOWN, (int)Keys.Down, 0);
}
}
Source : http://social.msdn.microsoft.com/Forums/windows/en-US/f2f0b213-d57a-46de-b924-e21b7ac0882e/programmatically-open-the-calendar-of-the-datetimepicker-control?forum=winforms
Usage:
dateTimePicker1.Open();
Warnings. This will not work if the dateTimePicker1 is a Control on DataGridView (ie if you try to make a pop-up DatePicker on the DGV). It does work if the Control is added to the Form instead. What will happen is that the synthesized cursor "down" event will be swallowed by the DGV, and will move the current cell pointer down one instead of drop-drop the Calendar of the DTP.
Source: https://social.msdn.microsoft.com/Forums/windows/en-US/f2f0b213-d57a-46de-b924-e21b7ac0882e/programmatically-open-the-calendar-of-the-datetimepicker-control?forum=winforms
Refer answer by David M Morton https://social.msdn.microsoft.com/profile/david%20m%20morton/?ws=usercard-mini
//DateTimePicker dtpicker
dtpicker.Select();
SendKeys.Send("%{DOWN}");
"%{DOWN}" Key combination - Alt key(%) +Down arrow
code to programmatically trigger key down event for datetimepicker
(particularly the event of click on the dropdown arrow in a datetimepicker)
Credit goes to astander for providing the solution, which makes a very nice extension:
using System.Linq;
using System.Runtime.InteropServices;
public static class Extensions {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int PostMessage(IntPtr hwnd, Int32 wMsg, Int32 wParam, Int32 lParam);
public static void Open(this DateTimePicker obj) {
const int WM_LBUTTONDOWN = 0x0201;
int width = obj.Width - 10;
int height = obj.Height / 2;
int lParam = width + height * 0x00010000; // VooDoo to shift height
PostMessage(obj.Handle, WM_LBUTTONDOWN, 1, lParam);
}
}
Usage:
dateTimePicker1.Open();
This way, you can reuse your Extension anytime you'd like, over and over, in any form using any DateTimePicker control.
The accepted answer is mostly correct, however you should also use:
PostMessage(dateTimePicker1.Handle, WM_LBUTTONUP, 1,lParam);
After posting the WM_LBUTTONDOWN event.
Also, obviously WM_LBUTTONUP must be previously defined:
const Int32 WM_LBUTTONUP = 0x0202;
So my answer is:
using System.Runtime.InteropServices;
//declares
[DllImport("user32.dll")]
private static extern bool PostMessage(
IntPtr hWnd, // handle to destination window
Int32 msg, // message
Int32 wParam, // first message parameter
Int32 lParam // second message parameter
);
const Int32 WM_LBUTTONDOWN = 0x0201;
const Int32 WM_LBUTTONUP = 0x0202;
//method to call dropdown
private void button1_Click(object sender, EventArgs e)
{
Int32 x = dateTimePicker1.Width - 10;
Int32 y = dateTimePicker1.Height / 2;
Int32 lParam = x + y * 0x00010000;
PostMessage(dateTimePicker1.Handle, WM_LBUTTONDOWN, 1,lParam);
PostMessage(dateTimePicker1.Handle, WM_LBUTTONUP, 1,lParam);
}
This avoids Mark Lakata's bug in Windows 7 and/or .NET 3.5.
The reasoning is simple: the original code simulates a mouse button down event, but doesn't get the mouse button up again as we would when we click the button.
In that regard, you can try it out yourself: if you press the left mouse button to open a DateTimePicker and don't release the button, you also won't be able to use the control.
Edit: Adapting jp2code's answer:
using System.Runtime.InteropServices;
public static class Extensions {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int PostMessage(IntPtr hwnd, Int32 wMsg, Int32 wParam, Int32 lParam);
public static void Open(this DateTimePicker obj) {
const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;
int width = obj.Width - 10;
int height = obj.Height / 2;
int lParam = width + height * 0x00010000; // VooDoo to shift height
PostMessage(obj.Handle, WM_LBUTTONDOWN, 1, lParam);
PostMessage(obj.Handle, WM_LBUTTONUP, 1, lParam);
}
}
I liked some of the previous ideas and finished with this (tested) Mix:
public static class Extensions {
public static void Open(this DateTimePicker obj)
{
obj.Select();
SendKeys.Send("%{DOWN}");
}
}
Usage:
dateTimePicker1.Open();
dateTimePicker2.Open();

Subscribing to mouse events of all controls in form

How can i easily catch the "mouse down" events of all the controls in a form, without manually subscribing to each and every event? (C#)
Something like the "KeyPreview" feature, but for the mouse events.
I found this to be the best solution for my purposes.
Create a new class derived from IMessageFilter:
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// do something
((YourMainForm)Form.ActiveForm).YourMainForm_Click(null, null);
}
return false;
}
}
Then in your main form add this to register the message filter:
GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
And add this function to do whatever you have to, in your form:
public void YourMainForm_Click(object sender, EventArgs e)
{
// do anything here...
}
Solution 1
Subscribing to each and every event on every control within a form is certainly the most simplest approach to take, since you just use the code given by Ramesh.
However, another technique involves overriding the default windows message processing method ("WndProc") on the parent control - in this case, the form that contains all the controls.
This has a side effect that you won't be able to detect when the mouse cursor moves over controls contained inside another parent control.
For example, you won't be able to detect when the mouse cursor is over a TextBox that is contained inside a TabControl. This is because the TabControl will continue to process all mouse events.
Solution 2
The following solution will overcome all issues in attempting to detect which control the mouse cursor is over using a technique known as windows hooks.
Hooks essentially allow us to trap mouse and keyboard events even before they are dispatched to the window with focus.
Here's a sample:
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStruct
{
public POINT pt;
public int hwnd;
public int hitTestCode;
public int dwExtraInfo;
}
[DllImport("user32.dll", SetLastError = true)]
static extern int SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hInstance, uint dwThreadId);
[DllImport("user32.dll", SetLastError= true)]
static extern int CallNextHookEx(int hook, int code, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern int GetLastError();
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static int hHook;
public Form1()
{
InitializeComponent();
hHook = SetWindowsHookEx(HookType.WH_MOUSE, MouseHookProc, IntPtr.Zero, (uint)GetCurrentThreadId());
if (hHook == 0)
MessageBox.Show("GetLastError: " + GetLastError());
}
private int MouseHookProc(int code, IntPtr wParam, IntPtr lParam)
{
//Marshall the data from the callback.
MouseHookStruct mouseInfo = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (code < 0)
{
return CallNextHookEx(hHook, code, wParam, lParam);
}
else
{
//Create a string variable that shows the current mouse coordinates.
String strCaption = "x = " + mouseInfo.pt.X.ToString("d") +
" y = " + mouseInfo.pt.Y.ToString("d");
//You must get the active form because it is a static function.
Form tempForm = Form.ActiveForm;
Control c = Control.FromHandle((IntPtr)mouseInfo.hwnd);
if (c != null)
label1.Text = c.Name;
else
label1.Text = "Control not found";
//Set the caption of the form.
tempForm.Text = strCaption;
return CallNextHookEx(hHook, code, wParam, lParam);
}
}
Other conttrols in the form cannot listen to the Mouse event handlers of the form. Because each control got its own mouse event listners.
But You can subscribe each controls mouse events to the forms mouse events
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.label1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.ListBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this way you can have single handler for all the controls mouse events.

Resources