【问题标题】:Capture ALL mouse clicks and key presses in a user control捕获用户控件中的所有鼠标点击和按键
【发布时间】:2020-08-12 13:41:44
【问题描述】:

我想捕获特定用户控件中的所有按键和鼠标点击。我需要这样做才能创建空闲检测,以便在用户有一段时间没有在用户控件中执行任何操作时解锁实体。

我的第一次尝试是PreProcessMessage 来检测windows 消息,但它似乎没有被调用?我的第二个是使用 DefWndProc,但不会为这些消息调用它。

如何为用户控件及其所有子控件获取 WM_KEYDOWNWM_MOUSEUP

更新

ProcessKeyPreview 用于检测键

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

    对于键盘方面,您可以尝试覆盖UserControl 中的ProcessCmdKey 事件。

    UserControl.cs:

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        //a key has been pressed within your user control
        //invoke event or some other action
        //...
    
        return base.ProcessCmdKey(ref msg, keyData);
    }
    

    对于鼠标,我想您的 UserControl 实现 IMessageFilter 接口的 PreFilterMessage 方法可能是前进的方向(尽管我不确定这是否特定于用户控件)。

    编辑

    我快速浏览了IMessageFilter,我认为我有一些可行的方法,尽管它看起来有点复杂。

    创建一个实现IMessageFilterMessageHandler

    class MessageHandler : IMessageFilter
    {
         private int WM_LBUTTONUP = 0x0202; //left mouse up
         private int WM_MBUTTONUP = 0x0208; //middle mouse up
         private int WM_RBUTTONUP = 0x0205; //right mouse up
    
         //stores the handles of the children controls (and actual user control)
         List<IntPtr> windowHandles = new List<IntPtr>();
         public MessageHandler(List<IntPtr> wnds)
         {
             windowHandles = wnds;
         }
    
    
         public bool PreFilterMessage(ref Message m)
         {
             //make sure that any click is within the user control's window or 
             //its child controls before considering it a click (required because IMessageFilter works application-wide)
             if (windowHandles.Contains(m.HWnd) && (m.Msg == WM_LBUTTONUP || m.Msg == WM_MBUTTONUP || m.Msg == WM_RBUTTONUP))
             {
                  Debug.WriteLine("Clicked");                
             }
             return false;
         }
     }
    

    在您的用户控件(或新类)中,您可以使用 P/Invoke 获取给定父级的子窗口。来自pinvoke.net

    public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
    
    [DllImport("user32")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
    
    private static List<IntPtr> GetChildWindows(IntPtr parent, bool addParent = true)
    {
        List<IntPtr> result = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(result);
        try
        {
            EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
            EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
        }
        finally
        {
            if (listHandle.IsAllocated)
               listHandle.Free();
        }
    
    
        if (addParent)
          result.Add(parent);
    
        return result;
    }
    
    
    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    {
        GCHandle gch = GCHandle.FromIntPtr(pointer);
        List<IntPtr> list = gch.Target as List<IntPtr>;
        if (list == null)
        {
            throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
        }
        list.Add(handle);
        //  You can modify this to check to see if you want to cancel the operation, then return a null here
        return true;
    }
    

    GetChildWindows 将返回我们需要的窗口。我对其进行了一些修改并添加了一个可选的addParent bool,它将用户控件本身添加到列表中。

    在您的UserControl.cs 构造函数中,我们可以注册MessageFilter 并传递句柄列表(或者,只要您知道用户控件的句柄,您就可以通过不同的表单添加它)。

    public MyControl()
    {
        InitializeComponent();
        Application.AddMessageFilter(new MessageHandler(GetChildWindows(this.Handle)));
    }
    

    您现在应该能够在您的用户控件及其任何子窗口中检测到三个鼠标按钮向上事件。当您的应用程序关闭或您的 UserControl 关闭/处置时,不要忘记致电 Application.RemoveMessageFilter

    正如我所说,这不是最直接的方法,但如果标准事件处理程序不适合您,它会捕获点击(我假设您已尝试订阅标准 mouseclick 事件)。

    【讨论】:

    • 不错的解决方案,你有什么新的要补充的吗?
    【解决方案2】:

    我会捕捉到以下事件:

    http://msdn.microsoft.com/en-us/library/system.windows.forms.control.click.aspx http://msdn.microsoft.com/en-us/library/system.windows.forms.control.textchanged.aspx 以及子控件的任何单击/更改事件。让所有事件处理程序调用相同的通用函数来保存最后一个操作的时间戳。

    Click += ClickHandler;
    TextChanged += ChangedHandler;
    
    foreach(var control in Controls)
    {
        control.Click += ClickHandler;
        control.TextChanged += ChangedHandler;
    }
    

    (以后不要忘记取消订阅这些控件,如果您有动态添加和删除控件,请适当处理订阅/取消订阅。)

    【讨论】:

    • 根据您的控件的复杂程度,您可能需要将其设为递归方法。
    • 编辑改变编码标准是怎么回事?每个地方对 this 和 var 的使用都有不同的政策。
    【解决方案3】:

    如果捕获它们无论哪个应用程序具有焦点都不是问题,您总是可以使用Gobal Keyboard/Mouse hook。在这里详细解释有点太复杂了,但是您可以随时参考my question 我做了类似的事情。

    【讨论】:

      【解决方案4】:

      对于键盘,如前所述,使用:

      protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
      {
          //invoke event or some other action
          return base.ProcessCmdKey(ref msg, keyData);
      }
      

      这应该解决鼠标事件,为您需要的每个现有鼠标事件创建新的:

      public new event MouseEventHandler MouseClick
      {
          add
          {
              base.MouseClick += value;
              foreach (Control control in Controls)
              {
                  control.MouseClick += value;
              }
          }
          remove
          {
              base.MouseClick -= value;
              foreach (Control control in Controls)
              {
                  control.MouseClick -= value;
              }
          }
      }
      
      public new event MouseEventHandler MouseDoubleClick
      {
          add
          {
              base.MouseDoubleClick += value;
              foreach (Control control in Controls)
              {
                  control.MouseDoubleClick += value;
              }
          }
          remove
          {
              base.MouseDoubleClick -= value;
              foreach (Control control in Controls)
              {
                  control.MouseDoubleClick -= value;
              }
          }
      }
      
      public new event EventHandler DoubleClick
      {
          add
          {
              base.DoubleClick += value;
              foreach (Control control in Controls)
              {
                  control.DoubleClick += value;
              }
          }
          remove
          {
              base.DoubleClick -= value;
              foreach (Control control in Controls)
              {
                  control.DoubleClick -= value;
              }
          }
      }
      

      【讨论】:

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