【问题标题】:How do I capture the mouse move event如何捕获鼠标移动事件
【发布时间】:2023-03-19 07:20:01
【问题描述】:

我想在我的主窗体中捕获鼠标移动事件。虽然我能够为主窗体连接 MouseEventHandler,但当光标位于 UserControl 或任何其他控件上时,该事件不再触发。如何确保我始终拥有鼠标位置。

【问题讨论】:

  • 按照 dup 线程中的说明使用 IMessageFilter。

标签: c# winforms


【解决方案1】:

您可以使用低级鼠标钩子。请参阅 this 示例并检查 HookCallback 中的 WM_MOUSEMOVE 消息。

您还可以使用 IMessageFilter 类来捕获鼠标事件并触发事件来获取位置(注意:这只会获取窗口上方的位置,而不是窗口之外):

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

【讨论】:

  • 这很好用,谢谢。我使用了您在此处发布的来源。然而,我确实注意到,即使鼠标没有移动,MouseMoved 函数也会被连续调用。当鼠标不在应用程序上方时,它会停止触发。
  • @SwDevMan81 人们如何确定在哪里查找消息 ID 的十六进制代码?我看到所有带有 User32 dll 调用的 Import 调用,我真的想知道在哪里可以找到有关此的信息
  • @SwDevMan81 就像兰迪所说的那样,即使鼠标不动,它也会不断调用函数,我测试了它出了什么问题?
  • 在我的简单应用程序中,这仅在鼠标在窗口内移动时才有效,也许它需要更高的权限才能从其他进程捕获鼠标事件? Win10 x64
【解决方案2】:

这里是解决方案。虽然我可以用类似的方法看到另一个答案。但既然我写了它,我想发布它。这里 MouseMessageFilter 有一个静态事件调用 MouseMove,您可以从应用程序中的任何位置订阅它。

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

【讨论】:

  • 这仅在 wm_mousemove 被发送到您的应用程序时才有效(这是当它的窗口的活动状态之一或鼠标光标下时)如果只对单个表单感兴趣,那么您需要检查该表单是否活跃。 .btw 它会在其他窗口也处于活动状态时触发。而且您不必在应用程序运行之前添加过滤器。
【解决方案3】:

我尝试了@SwDevMan81 提供的上述解决方案。虽然效果很好,但我也遇到了@Randy Gamage 提到的问题“即使鼠标没有移动,MouseMoved 函数也会被连续调用。当鼠标不在应用程序上方时它会停止触发”。 无论如何,这就是我想出的:

在表单构造函数中:

GlobalMouseHandler.MouseMovedEvent += GlobalMouseHandler_MouseMovedEvent;
Application.AddMessageFilter(new GlobalMouseHandler());

InitializeComponent();

事件处理程序:

private void GlobalMouseHandler_MouseMovedEvent(object sender, MouseEventArgs e)
{
   try
   {
      //Do whatever ...
   }
   catch { }
}

还有我稍微改动的 GlobalMouseHandler 类:

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
}

我希望有人可以使用它。

【讨论】:

  • 我弄清楚为什么它调用该方法两次,因为当第一次触发事件previousMousePosition 的值为x=0;y=0 并且当程序跳转到if(previousMousePosition != currentMousePoint) 时它返回true,我只需添加一个条件来检查previousMousePosition 是否具有默认值并在if 语句之前退出该函数。这样就解决了问题
【解决方案4】:

这是一个针对 WPF 的解决方案,它在整个应用程序上使用全局鼠标处理程序。由于 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;
}

【讨论】:

    【解决方案5】:
    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);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多