捕获用户控件中的所有鼠标单击和按键

时间:2013-04-22 12:59:27

标签: c# winforms

我想在特定用户控件中捕获所有按键和鼠标点击 。我需要这样做才能创建一个空闲检测,这样如果用户在用户控件中没有做任何事情,我可以解锁实体。

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

我如何获得WM_KEYDOWN& WM_MOUSEUP用户控件及其所有孩子?

更新

ProcessKeyPreview用于检测密钥

4 个答案:

答案 0 :(得分:4)

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

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,我认为我有一些可行的方法,虽然它看起来有点令人费解。

创建一个MessageHandler来实现IMessageFilter

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并传递句柄列表(或者只要您知道用户控件的句柄,就可以通过其他形式添加此句柄。) / p>

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

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

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

答案 1 :(得分:1)

我会发现以下事件:

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以及任何针对子控件的点击/更改事件。让所有事件处理程序调用相同的公共函数来保存最后一个操作的时间戳。

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

(不要忘记稍后取消订阅这些控件,如果您动态添加和删除控件,请相应地处理订阅/取消订阅。)

答案 2 :(得分:0)

如果抓住他们 哪个应用程序的重点不是问题,您可以随时使用Gobal Keyboard/Mouse hook。在这里详细解释有点太复杂,但你总是可以参考my question我做过类似的事情。

答案 3 :(得分:0)

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

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;
        }
    }
}