在Windows Forms应用程序中实现键盘快捷键的最佳方法?


280

我正在寻找在C#的Windows窗体应用程序中实现常见Windows键盘快捷键(例如Ctrl+ FCtrl+ N)的最佳方法。

该应用程序具有一个主窗体,该主窗体承载许多子窗体(一次一个)。当用户点击Ctrl+时F,我想显示一个自定义搜索表单。搜索表单将取决于应用程序中当前打开的子表单。

我当时在考虑在ChildForm_KeyDown事件中使用类似的方法

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

但这是行不通的。按下某个键时,该事件甚至不会触发。解决办法是什么?


真奇怪,Winforms似乎没有本机Windows API所具有的特定功能。
斯图尔特

Answers:


460

您可能忘记了将表单的KeyPreview属性设置为True。通用方法是重写ProcessCmdKey()方法:

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

这很好用,但是仅当表单是活动窗口时才为我检测到。任何人都知道如何绑定它,以便在任何窗口视图中都可以吗?
ʀʀʏ2012年

在某些情况下KeyPreview是必不可少的。例如,当按下快捷键时,需要禁止输入,但仍然允许MenuStrip触发单独的事件。ProcessCmdKey这种方法将迫使事件触发逻辑重复。
Saul 2014年

1
嗯,不,窗体的KeyDown事件处理程序与菜单项的Click事件处理程序完全不同。您仍然可以使用完全相同的方法,直接调用事件处理程序方法(不需要由事件专门调用它),也可以将公共逻辑重构为单独的方法。
Hans Passant 2014年

14
“什么是Ctrl + F?” 大声笑
kchad '18

73

在您的主表格上

  1. 设为KeyPreviewTrue
  2. 使用以下代码添加KeyDown事件处理程序

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.N)
        {
            SearchForm searchForm = new SearchForm();
            searchForm.Show();
        }
    }
    

4
+为,将KeyPreview设置为True ..缺少该信息
Usman Younas 2013年

20

最好的方法是使用菜单助记符,即在主窗体中具有菜单条目,并为其分配所需的键盘快捷键。然后,其他所有事情都在内部处理,而您要做的就是实现在Click该菜单项的事件处理程序中执行的适当操作。


1
问题在于主窗体不使用常见的Windows菜单。它使用用于显示子窗体的自定义导航面板。通过单击子表单上的ToolStripButton来调用搜索表单。
Rockcoder

menu mnemonics真实样本?
Kiquenet '19

1
@Kiquenetdocs.microsoft.com/ en
康拉德·鲁道夫

1
助记符是与快捷键不同的概念,操作方式也有重要区别。简而言之,助记符是带下划线的字符,用于使用键盘访问菜单,菜单命令和对话框控件。OTOH,快捷键是一种无需通过菜单即可访问命令的方法,而且它们可以是Ctrl + F或F5之类的任意击键,而不仅限于字符键。
斯图尔特

1
@Stewart这是正确的!我的回答并不是要暗示所有键盘快捷键都是助记符,而仅仅是菜单助记符是获得此功能的最简单方法。不幸的是,正如Rockcoder的评论所阐明的那样,这种解决方案在这里似乎不合适。我仍然尽可能推荐它作为首选解决方案。对于一般解决方案,汉斯接受的答案是前进的道路。
康拉德·鲁道夫

11

您甚至可以尝试以下示例:

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

8

如果您有菜单,则更改ShortcutKeys属性ToolStripMenuItem应该可以解决问题。

如果不是,则可以创建一个并将其visible属性设置为false。


不,我没有菜单。用于搜索的ToolStripButton实际上位于BindingNavigator控件上,因此添加菜单可能不是一个选项。
Rockcoder

6

在主表单中,您必须:

  • 确保将KeyPreview设置为true(默认为TRUE)
  • 添加MainForm_KeyDown(..)-您可以在此处设置所需的任何快捷方式。

此外,我已经在Google上找到了此邮件,并希望与仍在寻找答案的用户分享。(针对全球)

我认为您必须使用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
    }
}

进一步阅读此http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/


4

对于新手来说,汉斯的答案可能会更容易一些,所以这是我的版本。

您无需傻瓜KeyPreview,将其设置为false。要使用下面的代码,只需将其粘贴到您的代码下方form1_load并运行F5以查看其工作原理:

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

7
首先,推荐使用重写ProcessCmdKey来处理旨在执行类似命令的操作的按键,这是OP所要求的。其次,您误解了汉斯关于将KeyPreview设置为true的评论。他只是在告诉OP为什么他的技术不起作用。使用ProcessCmdKey不需要将KeyPreview设置为true。
RenniePet 2014年

2

在WinForm中,我们始终可以通过以下方式获取控制键状态:

bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.