Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Key Events: ProcessCmdKey

Tags:

c#

.net

winforms

I am trying to get some keyboard response happening on a little test Windows Form Application, and I have a rough solution, which is to override ProcessCmdKey. However, there are several issues I am encountering, and inconsistencies I am finding.

Different Events: Is there a way to tell, in the arguments ref Message msg, Keys keyData, whether the even is a KeyDown, a KeyUp, or a KeyPress?

KeyPress: Everywhere I have looked says that KeyPress, ie a repeated keyboard input, only happens for character keys, which the arrow keys are not. However, the event handler is being called as frequently, and in the same mannor/with the same behaviour, for the arrow keys as character keys. Is this in face a KeyPress event, or is it something else?

I would ideally like a way to handle, at form level, all keyboard events without letting them get passed to the controls on the form. However, all the documentation sufficiently confused me, and missed key points, so that I have not been able to complete this.

Help on any of these topics is appreciated. Thanks!

like image 630
3Pi Avatar asked May 06 '12 04:05

3Pi


2 Answers

Overriding ProcessCmdKey in your form is explicitly intended to allow you to implement custom short-cut keystroke handling beyond the built-in mnemonic handling in buttons and menu items.

It is only ever called on a key down event, before the control with the focus gets the KeyDown event and regardless of which client control has the focus. So not associated with KeyUp and not with KeyPress. You return true from your override when you recognize the key, after executing the shortcut function. This prevents the key from being processed any further, it won't generate any KeyDown/Press/Up events.

It is very rare to use the msg argument of the method, the msg.Msg value will only ever be WM_KEYDOWN or WM_SYSKEYDOWN with the latter message produced when the user held down the Alt key. Which you don't care about since you can always get if from the keyData argument. Like this:

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
        if (keyData == (Keys.Alt | Keys.F)) {
            // Alt+F pressed
            doSomething();
            return true;
        }
        return base.ProcessCmdKey(ref msg, keyData);
    }

The other modifiers you might want to check with the | operator as used here are Keys.Shift and Keys.Control. So (Keys.Shift | Keys.Control | Keys.F1) checks for Ctrl+Shift+F1. You can interpret the msg data when you want to do something unusual like checking for repeating keys. Check the MSDN docs for the WM_KEYDOWN notification. The msg.LParam value contains a bunch of info about the key stroke.

Note that you only get virtual keys in this method. Keys.F is the F key on the English keyboard layout but not necessarily the same letter for the key in the same location on the user's layout. Favor the function keys to avoid a documentation headache.

Key repetition is a feature of the keyboard controller and is not limited to typing keys. The arrow and function keys will certainly repeat when held down. You want to ignore KeyPress in this scenario. But if you assign a shortcut key for a key that's also a typing key (like Keys.F) then you want to always also check for a modifier key so you don't break controls like TextBox.

Last but not least, don't forget about the built-in support for mnemonics in the button and menu item controls. Writing their Text property like &OK produces a self-documenting shortcut without needing any code. Operated by the user, in this example, by typing Alt+O.

like image 160
Hans Passant Avatar answered Nov 02 '22 04:11

Hans Passant


The Message structure passed to ProcessCmdKey() contains the WINAPI message number in its Msg property:

  • WM_KEYDOWN is 0x100 (256),
  • WM_KEYUP is 0x101 (257),
  • WM_CHAR (roughly equivalent to KeyPress) is 0x102 (258),
  • WM_SYSKEYDOWN is 0x104 (260),
  • WM_SYSKEYUP is 0x105 (261).

Concerning your question about KeyPress, it is true that non-character keys such as the arrow keys do not generate WM_CHAR messages internally, but they do generate WM_KEYDOWN, and that message is also sent multiple times for repeated input.

Also note that I'm not sure ProcessCmdKey() is the right method to achieve what you want. The documentation describes it as only handling main menu command keys and MDI accelerators, which may only be a subset of the keys you want to catch. You might want to override ProcessKeyPreview() instead, which handles all the keyboard messages received by child controls.

like image 42
Frédéric Hamidi Avatar answered Nov 02 '22 06:11

Frédéric Hamidi