从每个组件中捕获鼠标事件

30

我在我的C# WinForm应用程序中遇到了鼠标事件问题。

我想获取应用程序中所有的鼠标点击,但我不想在每个子组件中放置一个监听器,也不想使用Windows鼠标钩子。

在Flash中,我可以在Stage上放置一个监听器来获取电影中的所有MouseEvents。

在C#中是否有这样的东西?全局MouseListener?


编辑:

我创建了这个类从IMessageFilter并使用Application.AddMessageFilter。

public class GlobalMouseHandler : IMessageFilter{

    private const int WM_LBUTTONDOWN = 0x201;

    public bool PreFilterMessage(ref Message m){
        if (m.Msg == WM_LBUTTONDOWN) {
            // Do stuffs
        }
        return false;
    }
}

将下面的代码放到需要监听全局点击的控件中:

GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
4个回答

16

一种简单的方法是通过调用Application.AddMessageFilter并编写实现IMessageFilter接口的类来添加消息循环过滤器。

通过IMessageFilter.PreFilterMessage,您的类可以查看通过您应用程序的消息循环传递的任何输入消息。 PreFilterMessage 还可以决定是否将这些消息传递给它们目标的特定控件。

这种方法引入的一个复杂性是必须处理 Windows 消息,通过传递给PreFilterMessage方法的 Message 结构体。 这意味着必须参考 Win32 文档中的 WM_LBUTTONDOWN, WM_MOUSEMOVEWM_LBUTTONUP 等,而不是常规的 MouseDownMouseMoveMouseUp 事件。


谢谢您的帮助!一切都正常工作。我使用了Application.AddMessageFilter,并取得了成功。我挂钩鼠标事件并创建了一个C# MouseEventHandler在WinForm级别分派事件到其他控件。 - Gustavo Cardoso

5

示例类

class CaptureEvents : IMessageFilter
{
    #region IMessageFilter Members
    public delegate void Callback(int message);
    public event Callback MessageReceived;

    IntPtr ownerWindow;
    Hashtable interestedMessages = null;
    CaptureEvents(IntPtr handle, int[] messages)
    {
        ownerWindow = handle;
        for(int c = 0; c < messages.Length ; c++)
        {
            interestedMessages[messages[c]] = 0;
        }
    }
    public bool PreFilterMessage(ref Message m)
    {
        if (m.HWnd == ownerWindow && interestedMessages.ContainsKey(m.Msg))
        {
            MessageReceived(m.Msg);
        }
        return true;
    }

    #endregion
}

3

如果你不想通过覆盖Form.PreProcessMessage或Form.WndProc来处理消息,那么你可以对Form进行子类化,以便将事件处理程序挂钩到表单上各个控件的所有MouseClick事件。
编辑:忘记递归遍历表单上控件的子控件。

    public class MousePreviewForm : Form
    {
      protected override void OnClosed(EventArgs e)
      {
         UnhookControl(this as Control);
         base.OnClosed(e);
      }

      protected override void OnLoad(EventArgs e)
      {
         base.OnLoad(e);

         HookControl(this as Control);
      }

      private void HookControl(Control controlToHook)
      {
         controlToHook.MouseClick += AllControlsMouseClick;
         foreach (Control ctl in controlToHook.Controls)
         {
            HookControl(ctl);
         }      
      }

      private void UnhookControl(Control controlToUnhook)
      {
         controlToUnhook.MouseClick -= AllControlsMouseClick;
         foreach (Control ctl in controlToUnhook.Controls)
         {
            UnhookControl(ctl);
         }
      }

      void AllControlsMouseClick(object sender, MouseEventArgs e)
      {
         //do clever stuff here...
         throw new NotImplementedException();
      }
   }

您的表单现在需要从MousePreviewForm而不是System.Windows.Forms.Form派生。


你甚至可以使用Form.OnControlAdded事件来应用AllControlsMouseClick处理程序。 - Tim Robinson
但我不能只循环遍历表单的第一个子级,我需要深入到每个控件中,并将AllControlsMouseClick添加到其子级中。 - Gustavo Cardoso
子组件的递归是一种好的策略。但我不想为每个控件添加监听器。对于鼠标事件,Application.AddMessageFilter 可以工作,但我认为你的想法也适用于其他类型的事件。感谢您的帮助! - Gustavo Cardoso

3

4
我尝试覆盖WinProc,但最终遇到了相同的问题:只有当我直接在窗体上点击时才能捕获鼠标点击事件。如果我在子控件上点击,则不会调用WinProc。 - Gustavo Cardoso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接