For the keyboard aspect you could try overriding the ProcessCmdKey
event within the 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);
}
For the mouse, I imagine your UserControl implementing the IMessageFilter interface's PreFilterMessage method may be the direction to head in (although I'm not sure if that can be specific to a user control).
Edit
I had a quick look at IMessageFilter
and I think I have something that may work although it does seem a bit convoluted.
Create a MessageHandler
that implements 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;
}
}
In your user control (or new class) you can use P/Invoke to get the child windows of a given parent. From 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;
}
The GetChildWindows
will return the windows we need. I modified it a bit and added an optional addParent
bool which will add the user control itself to the list.
In your UserControl.cs
constructor we can register the MessageFilter
and pass the list of handles (or you can add this via a different form as long as you know the handle of the user control).
public MyControl()
{
InitializeComponent();
Application.AddMessageFilter(new MessageHandler(GetChildWindows(this.Handle)));
}
You should now be able to detect the three mouse button up events in your user control and any of its child windows. Don't forget to call Application.RemoveMessageFilter
when your application is closed or when your UserControl
is closed/disposed.
As I say, this isn't the most straightforward way, but it'll capture the clicks if the standard event handlers aren't working for you (I assume you've tried subscribing to the standard mouseclick events).