310

I'm looking for a best way to implement common Windows keyboard shortcuts (for example Ctrl+F, Ctrl+N) in my Windows Forms application in C#.

The application has a main form which hosts many child forms (one at a time). When a user hits Ctrl+F, I'd like to show a custom search form. The search form would depend on the current open child form in the application.

I was thinking of using something like this in the ChildForm_KeyDown event:

   if (e.KeyCode == Keys.F && Control.ModifierKeys == Keys.Control)
        // Show search form

But this doesn't work. The event doesn't even fire when you press a key. What is the solution?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rockcoder
  • 8,289
  • 3
  • 32
  • 41
  • It's really strange that Winforms doesn't seem to have specific functionality for this like the native Windows API does. – Stewart Aug 19 '19 at 11:02

9 Answers9

501

You probably forgot to set the form's KeyPreview property to True. Overriding the ProcessCmdKey() method is the generic solution:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
  if (keyData == (Keys.Control | Keys.F)) {
    MessageBox.Show("What the Ctrl+F?");
    return true;
  }
  return base.ProcessCmdKey(ref msg, keyData);
}
Bridge
  • 29,818
  • 9
  • 60
  • 82
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 2
    This works well, but only detects it for me if the form is the active window. Anyone know how to bind it so in any window view it is possible? – Gaʀʀʏ Jul 18 '12 at 03:41
  • In some situations `KeyPreview` is quite indispensable. For example, when pressing a shortcut is required to suppress input but nevertheless to allow a separate `MenuStrip` event to fire. `ProcessCmdKey` approach would force the duplication of event firing logic. – Saul Jun 10 '14 at 11:13
  • 1
    Hmm, no, the Form's KeyDown event handler is quite distinct from a menu item's Click event handler. You still do the exact same way, either call the event handler method directly (no need for it to be exclusively called by an event) or refactor the common logic into a separate method. – Hans Passant Jun 10 '14 at 11:26
  • 28
    "What the Ctrl+F?" LOL – kchad May 19 '18 at 20:11
81

On your Main form

  1. Set KeyPreview to True
  2. Add KeyDown event handler with the following code

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
    
bluish
  • 26,356
  • 27
  • 122
  • 180
Almir
  • 1,158
  • 8
  • 9
20

The best way is to use menu mnemonics, i.e. to have menu entries in your main form that get assigned the keyboard shortcut you want. Then everything else is handled internally and all you have to do is to implement the appropriate action that gets executed in the Click event handler of that menu entry.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    The problem is that main form doesn't use common windows menus. It uses custom navigation panel that is used to show child forms. The search forms are invoked by click on the ToolStripButton on the child form. – Rockcoder Dec 30 '08 at 12:16
  • `menu mnemonics` real sample? – Kiquenet Feb 06 '19 at 09:33
  • 1
    @Kiquenet https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.menuitem.mnemonic?view=netframework-4.7.2 – Konrad Rudolph Feb 06 '19 at 13:11
  • 1
    Mnemonics are a distinct concept from shortcut keys, and there are important differences in how they operate. In short, mnemonics are the underlined characters used to access menus, menu commands and dialog controls using the keyboard. OTOH, shortcut keys are a way to access commands without going through the menus, and moreover they can be arbitrary keystrokes like Ctrl+F or F5, not restricted to character keys. – Stewart Aug 15 '19 at 08:49
  • 1
    @Stewart That is correct! My answer wasn’t trying to imply that all keyboard shortcuts are mnemonics, merely that menu mnemonics is the easiest way of getting this functionality. Unfortunately, as Rockcoder’s comment clarifies, this solution doesn’t seem appropriate here. I still recommend it as the preferable solution, where possible. For a general solution, Hans’ accepted answer is the way forward. – Konrad Rudolph Aug 15 '19 at 10:03
11

You can even try this example:

public class MDIParent : System.Windows.Forms.Form
{
    public bool NextTab()
    {
         // some code
    }

    public bool PreviousTab()
    {
         // some code
    }

    protected override bool ProcessCmdKey(ref Message message, Keys keys)
    {
        switch (keys)
        {
            case Keys.Control | Keys.Tab:
              {
                NextTab();
                return true;
              }
            case Keys.Control | Keys.Shift | Keys.Tab:
              {
                PreviousTab();
                return true;
              }
        }
        return base.ProcessCmdKey(ref message, keys);
    }
}

public class mySecondForm : System.Windows.Forms.Form
{
    // some code...
}
slavoo
  • 5,798
  • 64
  • 37
  • 39
Shilpa
  • 381
  • 1
  • 5
  • 18
8

If you have a menu then changing ShortcutKeys property of the ToolStripMenuItem should do the trick.

If not, you could create one and set its visible property to false.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Corin Blaikie
  • 17,718
  • 10
  • 37
  • 39
  • No I do't have a menu. ToolStripButton for search is actually located on the BindingNavigator control, so adding a menu is probably not an option. – Rockcoder Dec 30 '08 at 16:06
6

From the main Form, you have to:

  • Be sure you set KeyPreview to true( TRUE by default)
  • Add MainForm_KeyDown(..) - by which you can set here any shortcuts you want.

Additionally,I have found this on google and I wanted to share this to those who are still searching for answers. (for global)

I think you have to be using user32.dll

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == 0x0312)
    {
        /* Note that the three lines below are not needed if you only want to register one hotkey.
         * The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */

        Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);                  // The key of the hotkey that was pressed.
        KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF);       // The modifier of the hotkey that was pressed.
        int id = m.WParam.ToInt32();                                        // The id of the hotkey that was pressed.


        MessageBox.Show("Hotkey has been pressed!");
        // do something
    }
}

Further read this http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/

slavoo
  • 5,798
  • 64
  • 37
  • 39
Juran
  • 177
  • 1
  • 8
4

Hans's answer could be made a little easier for someone new to this, so here is my version.

You do not need to fool with KeyPreview, leave it set to false. To use the code below, just paste it below your form1_load and run with F5 to see it work:

protected override void OnKeyPress(KeyPressEventArgs ex)
{
    string xo = ex.KeyChar.ToString();

    if (xo == "q") //You pressed "q" key on the keyboard
    {
        Form2 f2 = new Form2();
        f2.Show();
    }
}
Community
  • 1
  • 1
  • 8
    First, overriding ProcessCmdKey is the recommended way of handling a keystroke intended to do some command-like operation, which is what the OP was asking about. Second, you've misunderstood Hans' comment about setting KeyPreview to true; he was just telling the OP why his technique wasn't working. Setting KeyPreview to true is not needed for using ProcessCmdKey. – RenniePet Jul 12 '14 at 05:58
2

In WinForm, we can always get the Control Key status by:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
s k
  • 4,342
  • 3
  • 42
  • 61
0

The VB.NET version of Hans' answer.

(There's a ProcessCmdKey function template in Visual Studio.)

  Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean

    If (keyData = (Keys.Control Or Keys.F)) Then
      ' call your sub here, like
      SearchDialog()
      Return True
    End If

    Return MyBase.ProcessCmdKey(msg, keyData)
  End Function

End Class
ourmandave
  • 1,505
  • 2
  • 15
  • 49