2010-01-14 116 views
10

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

+0

按照dup线程中的说明使用IMessageFilter。 – 2010-01-14 12:46:30

回答

18

您可以使用低级别的鼠标钩子。请参阅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 
    } 
} 
+1

这很好,谢谢。我使用了您在此处发布的来源。不过,我注意到,即使鼠标没有移动,MouseMoved函数也会不断调用。当鼠标不在应用程序上时,它停止发射。 – 2013-05-28 21:10:25

+0

@ SwDevMan81人们如何找出查找消息ID的十六进制代码的位置?我看到所有这些导入调用与User32 dll调用,我真的想知道在哪里可以找到有关此信息 – Dbl 2014-08-01 13:35:41

+1

@AndreasMüller - [键盘通知](http://msdn.microsoft.com/en-us/library/windows/desktop/ ff468861(v = vs.85).aspx)和[鼠标通知](http://msdn.microsoft.com/en-us/library/windows/desktop/ff468877(v = vs.85).aspx) – SwDevMan81 2014-08-01 14:27:44

6

这是解决方案。尽管我可以用类似的方法看到另一个答案。但是自从我写了它以后,我想发布它。这里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; 
    } 
} 
+0

只有当wm_mousemove发送给你的应用程序(当它的一个窗口的活动状态或它的鼠标光标下),如果只有单一表单感兴趣,那么你需要检查该表单是否有效。 .btw它会在其他窗口处于活动状态时触发。并且在运行应用程序之前,您不必添加过滤器。 – TakeMeAsAGuest 2015-07-09 19:32:21

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

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

在窗体的构造函数:

​​

的事件处理程序:

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 
} 

我希望有人可以使用它。

+0

这有一个很好的答案,但它调用方法两次 – 2016-08-16 08:53:44

+0

我弄清楚为什么它调用该方法两次,因为当第一次触发事件'previousMousePosition'有一个值'x = 0; y = 0',当程序跳转到'if(previousMousePosition!= currentMousePoint)'它返回true,我只是添加一个条件来检查'previousMousePosition'是否有一个默认值并在'if'语句之前退出函数。这解决了这个问题 – 2016-08-19 01:57:38