Recently I was working on a very complex user control with lots of child controls on it and wanted to be able to override the handling of the
keydown event in a single place (the main user control).
I added the keydown event to my user control and noticed that it never got fired. This seemed to be because the child controls were handling them instead and not passing them to the main control.
On a form, you can set
True which will allow the form to receive key events before they are passed to the control that has focus. Unfortunately, this is not available on user controls.
Some searching on the Internet revealed that the
ProcessKeyPreview event which when overridden in a user control will allow you to trap the keyboard messages before the child controls get them.
ProcessKeyPreview is not very friendly and passes you the Windows messages. This means you need to know the message number, handle repeating keys, handle control keys, etc.
I did a bit of reflecting on the framework and found that it's actually pretty easy to turn the messages into standard
keyup events which makes it much easier to code.
I thought someone may find it useful.
Using the Code
To use the code, simply paste it into your control. Then you just need to decide whether you need
keyup events and implement them as you see fit.
private struct MSG
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
public int pt_x;
public int pt_y;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool PeekMessage([In, Out] ref MSG msg,
HandleRef hwnd, int msgMin, int msgMax, int remove);
protected override bool ProcessKeyPreview(ref Message m)
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
const int WM_SYSCHAR = 0x106;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
const int WM_IME_CHAR = 0x286;
KeyEventArgs e = null;
if ((m.Msg != WM_CHAR) && (m.Msg != WM_SYSCHAR) && (m.Msg != WM_IME_CHAR))
e = new KeyEventArgs(((Keys)((int)((long)m.WParam))) | ModifierKeys);
if ((m.Msg == WM_KEYDOWN) || (m.Msg == WM_SYSKEYDOWN))
return base.ProcessKeyPreview(ref m);
private void RemovePendingMessages(int msgMin, int msgMax)
MSG msg = new MSG();
IntPtr handle = this.Handle;
while (PeekMessage(ref msg,
new HandleRef(this, handle), msgMin, msgMax, 1))
private void TrappedKeyDown(KeyEventArgs e)
if (e.KeyCode == Keys.A)
e.Handled = true;
e.SuppressKeyPress = true;
Points of Interest
Note that the
ProcessKeyPreview can return
false to indicate whether the keypress has been handled. However, if you are not handling it you should defer to the base
ModifierKeys is part of the base
Control class and returns a value indicating which of the modifier keys (SHIFT, CTRL, and ALT) is in a pressed state. You can set this in the
KeyEventArgs as per usual...
- Added Processing to remove messages if
e.SuppressKeyPress is set in the
- Changed message numbers to constants to make it easier to read
- Removed references to Form to avoid confusion and mentioned