Disadvantage of setting Form.KeyPreview = true?
Asked Answered
K

4

30

I wonder what the Form.KeyPreview property actually is good for? Why do it exist and what do I "risk" by setting it to true? I guess it must have some negative effect - otherwise it shouldn't exist at all (or at least be true by default)?

EDIT: I know perfectly well what it does. I'm asking why. Why do I have to set it to true in order to make the keyboard events fire? Why doesn't the keyboard events always fire for a form. What isn't just that the standard behavior?

The particular reason I ask is: I have just set KeyPreview = true in the base form of my app, which all other forms inherit from. Am I in for any nasty surprise?

Kazantzakis answered 5/3, 2010 at 12:37 Comment(3)
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. From msdn.microsoft.com/en-us/library/…Parvis
Yes. And why isn't this the default behavior?Vesuvian
I just came across a case where I didn't get a KeyDown event for the Escape key when focus was on a certain 3rd party control, even with KeyPreview set to True. Thankfully I found this post and tried overriding ProcessCmdKey for the custom control, which did allow me to get the key event before the control did. Thanks!Madid
W
79

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().

Wade answered 5/3, 2010 at 13:22 Comment(2)
I wonder if this answer is still current. I just used KeyPreview to process pressing of "Escape" to hide my form.Protostele
You probably missed the "for a dialog" clause. Making it always work, regardless of the kind of key or the way the form is displayed is the advantage of ProcessCmdKey().Wade
I
5

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.

Intermediacy answered 5/3, 2010 at 13:0 Comment(1)
Completely correct. Unfortunately it doesn't in any way answer what I asked.Vesuvian
M
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.

Mopboard answered 23/3, 2015 at 16:24 Comment(0)
G
2

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.

Gigantic answered 16/5, 2019 at 16:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.