The reason why keyboard handling is so complex, is because, well it’s just complex. Before a control can get called on the OnKeyDown/Press/Up event, we need to filter out several things
Shortcuts which execute menu commands (e.g. Control+O on a MenuItem)
Navigational keys which shift focus (Tab, arrow, escape)
Mnemonics which invoke click events/shift focus (e.g. “&Ok” on a button)
After all of these are weeded out, we can let the keyboard message go to the control directly to be interpreted.
The general flow of a keyboarding event is
User pushes “a” – get a WM_KEYDOWN - preprocessed – dispatched – KeyDown event fires
User holds “a” – get a WM_CHAR - preprocessed – dispatched – KeyPress event fires
User still holding “a” – get a 2nd WM_CHAR - preprocessed – dispatched – KeyPress event fires
User releases “a” – get a WM_KEYUP – preprocessed – dispatched – KeyUp event fires
If the keyboard message is “preprocessed”, it will not be dispatched/fire the key event.
Preprocessing happens in several methods within Windows Forms; depending if it’s a WM_KEYDOWN or a WM_CHAR, the functions called are slightly different. I’ll walk you through the preprocessing and dispatch stages of a keyboarding event.
Processing WM_KEYDOWN to become the Key Down event
1. Starting off:
Application (message pump) detects a WM_KEYDOWN or WM_SYSKEYDOWN window message for a control, and calls PreProcessMessage on the control.
For key down, preprocessing happens in this order
a. Check to see if this is a shortcut
o Call ProcessCmdKey to see if it is a key associated with a command (like a shortcut for a menu). If the shortcut isn’t found on the control’s context menu, ProcessCmdKey is called on the parent (all the way up to the form – which checks the main menu).
§ If the return value is true, preprocessing is complete, the key message is not dispatched, and the key event is not generated.
§ If no control in the control hierarchy knows about the shortcut, the return value is false.
b. Check to see if this is a normal character that should be dispatched to the control and fire KeyDown event
o Call IsInputKey to see if it is a key that should be processed in the control’s WndProc (thus raising the OnKeyDown event)
§ If the return value is true, the key is NOT considered “preprocessed” and the key message is immediately dispatched without checking to see if it’s a navigation key. (In other words, ProcessDialogKey is not called.)
c. Check to see if this is a navigation key (ESC, TAB, Return, Arrows)
o We get here only if ProcessCmdKey and IsInputKey returned false
o Call ProcessDialogKey to see if it’s a navigational character that the control is interested in handling, (typical examples are Tab, Return and Escape)
§ A container control would override this method to switch focus between child controls when a Tab or Arrow Key is pressed and return true to indicate that it has handled the key.
§ A dialog would close in response to the Enter and ESC keys and return true.
If the control does not handle the key, ProcessDialogKey is called on the parent, and so on up to the topmost control in the control hierarchy.
§ If the return value is false the key message is dispatched in order to generate the key event.
If the key message was not preprocessed, dispatch the message to the WndProc of the control. See notes below about firing key events.
Processing WM_CHAR to become the KeyPress event
1. Starting off:
Application (message pump) detects a WM_CHAR or WM_SYSCHAR window message for a control, and calls PreProcessMessage on the control.
a. Check to see if this is a normal character that should go to the control and fire KeyPress event
b. Check to see if this is a mnemonic
· call ProcessDialogChar is the analog to ProcessDialogKey. The same rules apply for searching up the parent hierarchy for a control that is interested in the key press.
· When ProcessDialogChar is called on a ContainerControl, it checks for mnemonics by calling ProcessMnemonic on itself. ContainerControl’s implementation of ProcessMnemonic iterates through the entire child hierarchy (in tab order) until a child returns true - indicating that it has handled the mnemonic.
· If ProcessMnemonic (hence ProcessDialogChar) returns true, the WM_CHAR message is not dispatched, and the key press event is not generated.
If after all that, we determine the key message has not preprocessed mnemonics, let the message go to the WndProc of the control. See notes below about firing key events.
Processing WM_KEYUP to become the KeyUp event
Application (message pump) detects a WM_KEYUP/WM_SYSKEYUP window message for a control, calls PreProcessMessage.
No preprocessing occurs for a key up.
The message goes to the WndProc of the control. See notes below about firing key events.
Dispatching/Firing Key Events
If the key message has not been preprocessed, it is dispatched to the WndProc of the control. Control calls ProcessKeyMessage, which let’s the parent control preview the message by calling ProcessKeyPreview. If a parent in the control hierarchy handles the key message by returning true, the no key event is generated.
If the key is not handled by the parent, ProcessKeyEventArgs is called. ProcessKeyEventArgs actually calls the OnKeyUp/OnKeyDown/OnKeyPress functions which then raise the KeyUp/KeyDown/KeyPress events.
So complicated!!!! What should I override when?
PreProcessMessage (Would not recommend overriding) - This method is called by the application's message loop to preprocess window messages before they are dispatched. For performance, it is better to override the specific method you need.
ProcessCmdKey – (Unlikely) Override when you’re doing your own shortcut handling that cannot be achieved using a System.Windows.Forms.MenuItem. The default implementation of this method will check the existing shortcuts on the control’s contextmenu, and so on up the parent chain. If the topmost control is a form, it will evaluate the shortcuts on the MainMenu.
IsInputKey – (Very Likely) Override when you have a key that’s normally a navigational key that you want to go to the control’s wndproc to be interpreted/fire OnKeyDown event
e.g. You want tab and return keys to be handled in a text box.
ProcessDialogKey – (Very Likely) Override when you’re doing special input/navigational handling for your child controls
e.g. A form wants to handle the enter key to click the ok/submit button.
e.g. A control desires special arrow key handling to change selection (change highlighting in a listbox style control)
e.g. A text box wants to handle the escape key to restore the original editing state
e.g. A container control or a panel wants to handle up/down arrow key handling between child controls
(note: you can get multiple key press events for one key down event – depending upon how long the user holds down the key)
IsInputChar – (Not-So-Likely) Override when you have a key that’s normally a navigational key that you want to go to the control’s WndProc to generate the OnKeyPress event
e.g. A list control wants to let the arrow keys out to the key press event so that each successive keypress accelerates switching between items (ok this one was a stretch)
ProcessDialogChar – (Not-So-Likely) Override when you’re doing special navigational handling that happens on a key press.
e.g. A treeview style control wants to handle each keypress to change selection, pressing and holding the “e” key down should skip between all the items starting with the letter “e” in the tree.
ProcessMnemonic – (Not-so-likely) Override when you’re writing a control that has special handling of mnemonics
e.g. A toolbar style control wants special evaluation for the mnemonics for buttons which are custom painted to its client area.
e.g. A label control wants to select the next control in the tab order when it’s mnemonic is invoked.
If handling mnemonics, use Control.IsMnemonic on the Text property to verify that the mnemonic matches the pressed key. Before performing the mnemonic action, use CanSelect to verify that the control is both visible and enabled.
The other functions
ProcessKeyPreview – (Not-so-likely) – if you’re a parent control and you want to prevent keystrokes from being dispatched to a child control, this function would work. This is here mainly for ease-of-use. It is MUCH better for a parent control to handle ProcessDialogKey instead of using this method.
ProcessKeyEventArgs – (Unlikely) – this simply calls the OnKey* methods. If the handled property key event is false Control.DefWndProc is called. Overriding is not recommended.
ProcessKeyMessage – (Unlikely) – this method is generally not useful to override.
The OnKey* Methods
OnKeyDown – raises the KeyDown event.
OnKeyUp – raises the KeyUp event.
OnKeyPress – raises the KeyPress event.