I would like to capture the mouse move event in my main form. Although I am able to wire up the MouseEventHandler
for the main form, the event no longer fires when the cursor is over a UserControl or any other control. How do I ensure that I always have the mouse position.
-
Use IMessageFilter as explained in the dup thread. – Hans Passant Jan 14 '10 at 12:46
5 Answers
You could use a low level mouse hook. See this example and check for the WM_MOUSEMOVE mesage in HookCallback.
You could also use the IMessageFilter class to catch the Mouse Events and trigger an event to get the position (note: this will only get the position over the window, not outside of it):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace GlobalMouseEvents
{
public partial class Form1 : Form
{
public Form1()
{
GlobalMouseHandler gmh = new GlobalMouseHandler();
gmh.TheMouseMoved += new MouseMovedEvent(gmh_TheMouseMoved);
Application.AddMessageFilter(gmh);
InitializeComponent();
}
void gmh_TheMouseMoved()
{
Point cur_pos = System.Windows.Forms.Cursor.Position;
System.Console.WriteLine(cur_pos);
}
}
public delegate void MouseMovedEvent();
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_MOUSEMOVE = 0x0200;
public event MouseMovedEvent TheMouseMoved;
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
if (TheMouseMoved != null)
{
TheMouseMoved();
}
}
// Always allow message to continue to the next filter control
return false;
}
#endregion
}
}

- 48,814
- 22
- 151
- 184
-
2This works great, thanks. I used the source you posted here. I did notice, however, that the MouseMoved function gets called continuously, even though the mouse is not moving. It stops firing when the mouse is not over the application. – Randy Gamage May 28 '13 at 21:10
-
1@SwDevMan81 How do people figure out where to look up hexcodes for message id's? I see all those Import calls with User32 dll calls and i am really wondering where to find information about this – Dbl Aug 01 '14 at 13:35
-
2@AndreasMüller - [Keyboard notifications](http://msdn.microsoft.com/en-us/library/windows/desktop/ff468861(v=vs.85).aspx) and [Mouse notifications](http://msdn.microsoft.com/en-us/library/windows/desktop/ff468877(v=vs.85).aspx) – SwDevMan81 Aug 01 '14 at 14:27
-
@SwDevMan81 like what randy said, it is continuously calling the function even though the mouse is not moving, i tested it what went wrong? – Shift 'n Tab Aug 16 '16 at 08:46
-
in my simple app this only works when the mouse is moved within the window, maybe it need higher privileges to capture the mouseevent from other processes? Win10 x64 – René W. Aug 10 '17 at 10:47
-
@RandyGamage You can set a variable to the mouse's last position inside **gmh_TheMouseMoved()**, then check if the mouse has actually moved since last time. If not, don't do anything. – DCOPTimDowd Aug 12 '19 at 18:41
Here is the solution. Although I can see another answer with a similar approach. But since I wrote it I want to post it. Here MouseMessageFilter has a static event call MouseMove which you can subscribe from anywhere within the application.
static class Program
{
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.AddMessageFilter(new MouseMessageFilter());
MouseMessageFilter.MouseMove += new MouseEventHandler(OnGlobalMouseMove);
Application.Run(new MainForm());
}
static void OnGlobalMouseMove(object sender, MouseEventArgs e) {
Console.WriteLine(e.Location.ToString());
}
}
class MouseMessageFilter : IMessageFilter
{
public static event MouseEventHandler MouseMove = delegate { };
const int WM_MOUSEMOVE = 0x0200;
public bool PreFilterMessage(ref Message m) {
if (m.Msg == WM_MOUSEMOVE) {
Point mousePosition = Control.MousePosition;
MouseMove(null, new MouseEventArgs(
MouseButtons.None, 0, mousePosition.X, mousePosition.Y,0));
}
return false;
}
}
-
this only works if wm_mousemove is sent to your application (which is when one of its window's active state or its under the mouse cursor) if only single form is interested, then you need to check if that form is active. .btw it fires when other windows' are active too. and you dont have to add your filter before application run. – TakeMeAsAGuest Jul 09 '15 at 19:32
I tried the above mentioned solutoution provided by @SwDevMan81. Although it worked nicely, I also had the issue @Randy Gamage mentioned "that the MouseMoved function gets called continuously, even though the mouse is not moving. It stops firing when the mouse is not over the application". In any case this is what I came up with:
In the form constructor:
GlobalMouseHandler.MouseMovedEvent += GlobalMouseHandler_MouseMovedEvent;
Application.AddMessageFilter(new GlobalMouseHandler());
InitializeComponent();
The event handler:
private void GlobalMouseHandler_MouseMovedEvent(object sender, MouseEventArgs e)
{
try
{
//Do whatever ...
}
catch { }
}
And my slightly altered GlobalMouseHandler class:
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_MOUSEMOVE = 0x0200;
private System.Drawing.Point previousMousePosition = new System.Drawing.Point();
public static event EventHandler<MouseEventArgs> MouseMovedEvent = delegate { };
#region IMessageFilter Members
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
System.Drawing.Point currentMousePoint = Control.MousePosition;
if (previousMousePosition != currentMousePoint)
{
previousMousePosition = currentMousePoint;
MouseMovedEvent(this, new MouseEventArgs(MouseButtons.None, 0, currentMousePoint.X, currentMousePoint.Y, 0));
}
}
// Always allow message to continue to the next filter control
return false;
}
#endregion
}
I hope somebody can use it.

- 293
- 4
- 12
-
I figure out why it calls the method twice because when the event fired for the first time `previousMousePosition` has a value of `x=0;y=0` and when the program jump to `if(previousMousePosition != currentMousePoint)` it returns true, i just add a condition that will check if the `previousMousePosition` has a default value and exit the function before the `if` statement. This solves the problem – Shift 'n Tab Aug 19 '16 at 01:57
Here is a solution for WPF with a global mouse handler over the whole application. I use this also due to other mouse issues in WPF.
using System.Windows.Interop;
private const int WM_MOUSEMOVE = 0x0200;
public delegate void Del_MouseMovedEvent(Point mousePosition);
// Relative to this control, the mouse position will calculated
public IInputElement Elmt_MouseMovedRelativeElement = null;
// !! This is static; needs special treatment in a multithreaded application !!
public static event Del_MouseMovedEvent Evt_TheMouseMoved = null;
// your main function call
public MyMainWindows()
{
// install the windows message filter first
ComponentDispatcher.ThreadFilterMessage += ComponentDispatcher_ThreadFilterMessage;
InitializeComponent();
...
}
// filtering the windows messages
private void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)
{
if(msg.message == WM_MOUSEMOVE)
{
this.Evt_TheMouseMoved?.Invoke(Mouse.GetPosition(this.Elmt_MouseMovedRelativeElement));
}
}
// individual event for mouse movement
private void MyMouseMove(Point mousePoint)
{
// called on every mouse move when event is assigned
Console.WriteLine(mousePoint.X + " " + mousePoint.Y);
}
private void AnyFunctionDeeperInTheCode()
{
// assign the handler to the static var of the main window
MyMainWindows.Evt_TheMouseMoved += MyMouseMove;
// set the element / control to which the mouse position should be calculated;
MyMainWindows.Elmt_MouseMovedRelativeElement = this;
...
// undassign the handler from the static var of the main window
MyMainWindows.Evt_TheMouseMoved -= MyMouseMove;
}

- 2,508
- 3
- 35
- 52
public partial class frmCaptureMouse : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetCapture(IntPtr hWnd);
public frmCaptureMouse()
{
InitializeComponent();
}
private void frmCaptureMouse_MouseMove(object sender, MouseEventArgs e)
{
try
{
lblCoords.Text = e.Location.X.ToString() + ", " + e.Location.Y.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnCapture_Click(object sender, EventArgs e)
{
try
{
SetCapture(this.Handle);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

- 2,741
- 3
- 25
- 29