简体   繁体   中英

How do I capture the mouse move event

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.

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

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM