R – Subscribing to mouse events of all controls in form

eventsmousewinforms

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.

Best Answer

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