Disadvantage of setting Form.KeyPreview = true?
Solution 1:
Form.KeyPreview is a bit of an anachronism, inherited from the Visual Basic object model for form design. Back in the VB6 days, you needed KeyPreview to be able to implement short-cut keystrokes. That isn't needed anymore in Windows Forms, overriding the ProcessCmdKey() is the better solution:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == (Keys.Control | Keys.F)) {
DoSomething(); // Implement the Ctrl+F short-cut keystroke
return true; // This keystroke was handled, don't pass to the control with the focus
}
return base.ProcessCmdKey(ref msg, keyData);
}
But KeyPreview was supported to help the legion of VB6 programmers switch to .NET back in the early 2000's. The point of KeyPreview or ProcessCmdKey() is to allow your UI to respond to shortcut keystrokes. Keyboard messages are normally sent to the control that has the focus. The Windows Forms message loop allows code to have a peek at that message before the control sees it. That's important for short-cut keys, implementing the KeyDown event for every control that might get the focus to detect them is very impractical.
Setting KeyPreview to True doesn't cause problems. The form's KeyDown event will run, it will only have an affect if it has code that does something with the keystroke. But do beware that it closely follows the VB6 usage, you can't see the kind of keystrokes that are used for navigation. Like the cursor keys and Tab, Escape and Enter for a dialog. Not a problem with ProcessCmdKey().
Solution 2:
From MSDN
When this property is set to true, the form will receive all KeyPress, KeyDown, and KeyUp events. After the form's event handlers have completed processing the keystroke, the keystroke is then assigned to the control with focus. For example, if the KeyPreview property is set to true and the currently selected control is a TextBox, after the keystroke is handled by the event handlers of the form the TextBox control will receive the key that was pressed. To handle keyboard events only at the form level and not allow controls to receive keyboard events, set the KeyPressEventArgs.Handled property in your form's KeyPress event handler to true.
You can use this property to process most keystrokes in your application and either handle the keystroke or call the appropriate control to handle the keystroke. For example, when an application uses function keys, you might want to process the keystrokes at the form level rather than writing code for each control that might receive keystroke events.
Basically when you set it to true, your form can process key events as well as your controls.
E.G User presses K key, the forms event handlers are called(Key Down, Key Up, Key Pressed) and then the event handlers on the currently active control are called.
EDIT: No there are no disadvantages or nasty suprises. The only thing i can think of is a very tiny performance decrease as it needs to check for event handles on the form for each KeyDown,KeyUp, KeyPressed. Apart from that unless you're adding event handlers to the form, and doing something that might cause problems. you're perfectly fine. If you don't need to globally handle key events except on controls then I would suggest you leave this as false to prevent the extra checks. On modern PC's this wouldnt have a visible difference.
Solution 3:
The standard Windows event model is that the window with the keyboard focus gets all keyboard events. Remember that in Windows, everything is a window - a 'control' is just a window that is a child of another window. It's up to that window to fire messages to its parent if it chooses to do so, when certain keys are pressed.
To standardise navigation between controls on a dialog box, Windows also provides the 'dialog manager'. In native code, for modal dialogs this is handled by the modal message loop inside the DialogBox
function. For modeless dialogs you have to call IsDialogMessage
within your own message loop. This is how it steals the Tab and cursor keys to navigate between controls, and Enter to press the default button. This has the opposite effect of not allowing the controls to handle Enter by default, which multi-line edit controls would normally handle. To discover whether a control wants to handle a key, the dialog manager code sends the focused control a WM_GETDLGCODE
message; if the control responds appropriately the dialog manager returns FALSE
allowing DispatchMessage
to actually deliver it to the window procedure, otherwise the dialog manager does its own thing.
Windows Forms largely just wraps up the old native controls, so it has to conform to Win32's event model. It implements the same dialog manager approach - hence why it doesn't, by default, allow you to see Tab, Return and the cursor keys.
The recommended approach, if you want to handle one of those keys, is to override PreviewKeyDown
and set the PreviewKeyDownEventArgs
IsInputKey
property to true
.
Solution 4:
A simple and trivial, though practical reason:
In a game like Space Invaders https://www.mooict.com/c-tutorial-create-a-full-space-invaders-game-using-visual-studio/ the user repeatedly hammers the space bar to vaporize aliens. When the last invader is gone, a text box pops up to say, "good job". The user's still twitching thumb hits the space bar (or maybe just the keyboard buffer releases?) and the congrats MessageBox vanishes before it can be read. I couldn't see a work around because of how Forms handle button/space bar clicks.
My custom dialog uses keypreview to preprocess keystrokes sent to the GameOverDialog to ignore any space bar taps. The user has to close with a mouse click or Enter. This is a just a FixedDialog with a "you win" label and an [OK] button.
public partial class GameOverDialog : Form
{
public GameOverDialog()
{
InitializeComponent();
this.MaximizeBox = false;
this.MinimizeBox = false;
}
// keyhandler keypreview = true
private void SpaceDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
return;
}
}
private void SpaceUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
return;
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
}
Also, an interesting option that I've not tested: If you think about it, this would be a great way to embed cheats, hidden messages, etc. into innocuous [OK] dialogs or any Form that allows key preview.