我想捕获特定用户控件中的所有按键和鼠标点击。我需要这样做才能创建空闲检测,以便如果用户有一段时间没有在用户控件中执行任何操作,我可以解锁实体。
我的第一次尝试是PreProcessMessage
检测 Windows 消息,但它似乎没有被调用?我的第二个是使用 DefWndProc,但不会为这些消息调用它。
我怎样才能为用户控件及其所有子控件获取WM_KEYDOWN
& ?WM_MOUSEUP
更新
ProcessKeyPreview
用于检测键
对于键盘方面,您可以尝试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);
}
对于鼠标,我想您实现IMessageFilter接口的PreFilterMessage方法的 UserControl 可能是前进的方向(尽管我不确定这是否特定于用户控件)。
编辑
我快速浏览了一下,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
布尔值,它将用户控件本身添加到列表中。
在您的UserControl.cs
构造函数中,我们可以注册MessageFilter
并传递句柄列表(或者,只要您知道用户控件的句柄,您就可以通过不同的形式添加它)。
public MyControl()
{
InitializeComponent();
Application.AddMessageFilter(new MessageHandler(GetChildWindows(this.Handle)));
}
您现在应该能够检测到用户控件及其任何子窗口中的三个鼠标按钮向上事件。不要忘记Application.RemoveMessageFilter
在您的应用程序关闭或您的应用程序UserControl
关闭/处置时致电。
正如我所说,这不是最直接的方法,但如果标准事件处理程序不适合您,它会捕获点击(我假设您已经尝试订阅标准 mouseclick 事件)。
我会捕捉到以下事件:
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;
}
(不要忘记稍后取消订阅这些控件,如果您有动态添加和删除控件,请适当处理订阅/取消订阅。)
如果无论哪个应用程序具有焦点都可以捕获它们,那么您始终可以使用Gobal Keyboard/Mouse hook。在这里详细解释有点太复杂了,但你总是可以参考我的问题,我做了类似的事情。
如前所述,对于键盘,请使用:
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;
}
}
}