【发布时间】:2023-03-19 07:20:01
【问题描述】:
我想在我的主窗体中捕获鼠标移动事件。虽然我能够为主窗体连接 MouseEventHandler,但当光标位于 UserControl 或任何其他控件上时,该事件不再触发。如何确保我始终拥有鼠标位置。
【问题讨论】:
-
按照 dup 线程中的说明使用 IMessageFilter。
我想在我的主窗体中捕获鼠标移动事件。虽然我能够为主窗体连接 MouseEventHandler,但当光标位于 UserControl 或任何其他控件上时,该事件不再触发。如何确保我始终拥有鼠标位置。
【问题讨论】:
您可以使用低级鼠标钩子。请参阅 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
}
}
【讨论】:
这里是解决方案。虽然我可以用类似的方法看到另一个答案。但既然我写了它,我想发布它。这里 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;
}
}
【讨论】:
我尝试了@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 语句之前退出该函数。这样就解决了问题
这是一个针对 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;
}
【讨论】:
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);
}
}
}
【讨论】: