ChatGPT解决这个技术问题 Extra ChatGPT

Best way to implement keyboard shortcuts in a Windows Forms application?

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?

It's really strange that Winforms doesn't seem to have specific functionality for this like the native Windows API does.

B
Bridge

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);
}

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?
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.
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.
"What the Ctrl+F?" LOL
b
bluish

On your Main form

Set KeyPreview to True 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(); } }


+ for , Set KeyPreview to True .. was missing that
K
Konrad Rudolph

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.


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.
menu mnemonics real sample?
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 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.
s
slavoo

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...
}

P
Peter Mortensen

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.


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.
s
slavoo

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/


C
Community

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();
    }
}

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.
s
s k

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

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;

o
ourmandave

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