c# - 捕获用户控件中的所有鼠标点击和按键操作

标签 c# winforms

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

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

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

更新

ProcessKeyPreview 用于检测键

最佳答案

对于键盘方面,您可以尝试覆盖 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

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

关于c# - 捕获用户控件中的所有鼠标点击和按键操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16147714/

相关文章:

c# - 隐式转换为 DateTime?和 op_Equality

c# - 当用户从上下文菜单中选择 "Copy"时如何将数据复制到剪贴板

c# - 强制 WinForms 重新生成 .designer.cs 文件

c# - Silverlight 中位图和图形(在 Windows 窗体中找到)的等价物是什么?

c# - WPF 的自动完成组合框

c# - 在 C#/WPF 中使用 Canvas 遮盖另一个 Canvas

c# - 如何使用代码在 C# 中调用 materialDesign 组件并添加到 wpf 设计中?

c# - TableLayoutPanel 标签对齐到左上角?

c# - 有什么方法可以在发生崩溃时删除通知图标?

c# - 下拉列表 SelectedItems 回发不存在