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.
Related
I have tried looking into this for a while now and have not been able to come up with an answer that has worked. I am trying to register my WPF C# application to listen for Ctrl+C from anywhere while the application is running. The code below is part of my application and does trigger when the user presses Ctrl+C, however, the contents of the Clipboard are stale (the contents are from a previous copy before the application was running). The AnimateWindow function is being called so I know my hotkey registration is working.
So, my issue is that it appears registering for the hotkey below is overriding the copy functionality. I would like to keep this functionality intact as it is part of how my application works. I need to take what was copied and use that data for a database query and to fill a TextBox.
This is just a snippet of the entire project, please let me know if additional example code is needed to help answer. Thank you!
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
// CTRL + C Hotkey
private const int COPY_ID = 9001;
private const uint MOD_CONTROL = 0x0002; // Modifier: Control
private const uint VK_C = 0x43; // 'C' key
/* Registering the hotkeys to be monitored */
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_windowHandle = new WindowInteropHelper(this).Handle;
_source = HwndSource.FromHwnd(_windowHandle);
_source.AddHook(HwndHook);
RegisterHotKey(_windowHandle, COPY_ID, MOD_CONTROL, VK_C); // 'C' key
}
/* The hook for pressing a hotkey */
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
switch (msg)
{
case WM_HOTKEY:
switch (wParam.ToInt32())
{
case COPY_ID:
AnimateWindow();
break;
}
}
}
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 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;
}
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();
just curious if you know of any way to setup a drag boundary for a window?
It would be nice to have these properties:
Me.MinLeft = 10
Me.MinTop = 10
Me.MaxLeft = 150
Me.MaxTop = 150
Those are made up properties, btw, which would be nice to have.
I know I could probably setup a timer to fire ever 10th of a second and check the left and top and then move it back if it's over. But it would be more elegant to have the window act like it hit a wall and can't go any farther, like moving to the edge of the screen or something similar.
Edit: There seems to be some confusion somewhere, the point I'm trying to make is in the paragraph above, dragging, not re-sizing.
Here is teh "magic" you need to create this functionality, all you have to do is set the Window_SourceInitialized method to the window's SourceInitialized event and insert you logic where the big comment is.
I combined this code from several sources, so there could be some syntax errors in it.
internal enum WM
{
WINDOWPOSCHANGING = 0x0046,
}
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
private void Window_SourceInitialized(object sender, EventArgs ea)
{
HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
hwndSource.AddHook(DragHook);
}
private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
switch ((WM)msg)
{
case WM.WINDOWPOSCHANGING:
{
WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
if ((pos.flags & (int)SWP.NOMOVE) != 0)
{
return IntPtr.Zero;
}
Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
if (wnd == null)
{
return IntPtr.Zero;
}
bool changedPos = false;
// ***********************
// Here you check the values inside the pos structure
// if you want to override tehm just change the pos
// structure and set changedPos to true
// ***********************
if (!changedPos)
{
return IntPtr.Zero;
}
Marshal.StructureToPtr(pos, lParam, true);
handeled = true;
}
break;
}
return IntPtr.Zero;
}
As I have no doubt that Nir's answer will work spending a little time implementing it, I was able to do what I wanted a little bit more elegant with this code:
Private Sub myWindow_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
Dim primaryBounds As System.Drawing.Rectangle = Windows.Forms.Screen.PrimaryScreen.Bounds
Dim windowBounds As System.Drawing.Rectangle = New System.Drawing.Rectangle(CInt(Me.Left), CInt(Me.Top), CInt(Me.Width), CInt(Me.Height))
If (windowBounds.Left < 0) Then
windowBounds = New System.Drawing.Rectangle(0, windowBounds.Top, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Right > primaryBounds.Right) Then
windowBounds = New System.Drawing.Rectangle(primaryBounds.Right - windowBounds.Width, windowBounds.Top, windowBounds.Width, windowBounds.Height)
End If
If (windowBounds.Top < 0) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, 0, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Bottom > primaryBounds.Bottom) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, primaryBounds.Bottom - windowBounds.Height, windowBounds.Width, windowBounds.Height)
End If
Me.Left = windowBounds.Left
Me.Top = windowBounds.Top
End Sub
This made the window being dragged stay within the primary screen (whole window), but you could easily change the bounds to whatever values you needed.
There are dependency properties for WPF's Window for this purpose.
Here they are:
Window.MaxWidth
Window.MaxHeight
These properties will constrain the size of the Window, just like the WinForm's Form.
Maybe you could handle PreviewMouseMove (either the event or override the corresponding protected method) and set e.Handled = true whenever the mouse movement would cause the window to move outside the region you want to constrain it to.
This seems like the most logical, WPF-like way of doing this.