窗体不响应 KeyDown 事件

我已经在我的 Windows 窗体项目上工作了一段时间,我决定尝试一下键盘快捷键。读了一会儿之后,我想我只需要编写一个事件处理程序,并将其绑定到表单的 KeyDown 事件:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.Alt && e.KeyCode == Keys.O)
{
MessageBox.Show("Ctrl+Alt+O: magic!");
}
}

我这样做的好方法是打开 Visual Studio 设计器的 Properties 面板,然后双击窗体的 KeyDown 事件以生成 Form1_KeyDown事件处理程序。但在测试我的应用程序时,表单根本不响应 Ctrl + Alt + O的快捷键。VisualStudio 设计器确实生成了将事件处理程序绑定到窗体的代码,不过:

private void InitializeComponent()
{
// ...


this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);


// ...
}

所以我尝试向处理程序添加一个 Console.WriteLine()调用来检查它是否被调用,但是也没有运气。

另外,我尝试在事件绑定调用上设置一个断点(如上所示) ,发现程序能够很好地到达该断点。但是我在方法定义本身中设置的任何断点都不会到达。

为了确保前几个步骤是正确的,我试着重复这些步骤:

  • 同一溶液中的新形式。
    同样的问题: 当我按下 Ctrl + Alt + O快捷键时,表单没有响应,调试器甚至没有进入事件处理程序。再试一次,它起作用了。译注:

  • 全新的 WinForms 解决方案。
    它工作得很完美: 出现消息对话框(Console.WriteLine()调用也可以工作)

因此,我在这里完全迷路了。是什么阻止这个项目中的所有窗体接收 KeyDown 事件?

118036 次浏览

Does your form have KeyPreview property set to true?

Form.KeyPreview Property

Gets or sets a value indicating whether the form will receive key events before the event is passed to the control that has focus.

http://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview.aspx

Try setting the KeyPreview property on your form to true. This worked for me for registering key presses.

The most common piece of advice for this problem on StackOverflow and the MSDN1, 2 (including the accepted answer here) is quick and easy:

KeyDown events are triggered on a Form as long as its KeyPreview property is set to true

That's adequate for most purposes, but it's risky for two reasons:

  1. KeyDown handlers do not see all keys. Specifically, "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."

  2. There are a few different ways to intercept key events, and they all happen in sequence. KeyDown is handled last. Hence, KeyPreview isn't much of a preview, and the event could be silenced at a few stops on the way.

(Credit to @HansPassant for those points.)

Instead, override ProcessCmdKey in your Form:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.Up)
{
// Handle key at form level.
// Do not send event to focused control by returning true.
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}

That way, all keys are visible to the method, and the method is first in line to see the event.

Note that you still have control over whether or not focused controls see the KeyDown event. Just return true to block the subsequent KeyDown event, rather than setting KeyPressEventArgs.Handled to true as you would in a KeyDown event handler. Here is an article with more details.