设置Form.KeyPreview = true的缺点?

时间:2010-03-05 12:37:36

标签: .net windows winforms

我想知道Form.KeyPreview属性实际上有什么用处?为什么它存在以及通过将其设置为true来“冒险”?我想它必须有一些负面效果 - 否则根本不存在(或者至少默认为真)?

编辑:我完全清楚它的作用。我问为什么。为什么我必须将其设置为true才能使键盘事件触发?为什么键盘事件始终不会触发表单。什么不仅仅是标准行为?

我问的特殊原因是:我刚刚在我的应用程序的基本形式中设置了KeyPreview = true,其他所有形式都继承自。我是否有任何令人讨厌的惊喜?

4 个答案:

答案 0 :(得分:64)

Form.KeyPreview有点不合时宜,继承自表单设计的Visual Basic对象模型。回到VB6时代,您需要KeyPreview才能实现快捷键击。在Windows窗体中不再需要它,覆盖ProcessCmdKey()是更好的解决方案:

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

但支持KeyPreview以帮助VB6程序员在2000年初转回.NET。 KeyPreviewProcessCmdKey()的要点是允许您的UI响应快捷键击。键盘消息通常发送到具有焦点的控件。 Windows窗体消息循环允许代码在控件看到之前查看该消息。这对于快捷键非常重要,为每个控件实现KeyDown事件可能会让人注意检测它们是非常不切实际的。

KeyPreview设置为True不会导致问题。表单的KeyDown事件将会运行,只有具有按键操作的代码才会产生影响。但要注意它紧跟VB6的使用,你无法看到用于导航的击键类型。与光标键和TabEscapeEnter类似,用于对话框。 ProcessCmdKey()没有问题。

答案 1 :(得分:4)

来自MSDN

  

当此属性设置为true时,   表单将收到所有KeyPress,   KeyDown和KeyUp事件。之后   form的事件处理程序已经完成   处理击键,   然后将击键分配给   控制焦点。例如,如果   KeyPreview属性设置为true   并且当前选择的控件是   按键后的TextBox   由事件处理程序处理   表单将收到TextBox控件   被按下的钥匙。处理   键盘事件仅在表单级别   并且不允许控件接收   键盘事件,设置   KeyPressEventArgs.Handled属性   你的表单的KeyPress事件处理程序   真。

     

您可以使用此属性进行处理   您应用程序中的大多数击键   并处理击键或   调用适当的控件来处理   击键。例如,当一个   应用程序使用功能键,你   可能想要处理击键   在形式层面而不是写作   可能的每个控件的代码   接收击键事件。

基本上,当您将其设置为true时,您的表单可以处理关键事件以及控件。

E.G用户按下K键,调用表单事件处理程序(Key Down,Key Up,Key Pressed),然后调用当前活动控件上的事件处理程序。

编辑:没有缺点或令人讨厌的惊喜。我唯一能想到的是性能下降非常小,因为它需要检查表单上每个KeyDown,KeyUp,KeyPressed的事件句柄。除此之外,除非您在表单中添加事件处理程序,并执行可能导致问题的操作。你很好。如果您不需要全局处理除控件之外的关键事件,那么我建议您将其保留为false以防止额外检查。在现代PC上,这不会有明显的差异。

答案 2 :(得分:2)

标准Windows事件模型是具有键盘焦点的窗口获取所有键盘事件。请记住,在Windows中,一切都是一个窗口 - 一个控件'只是一个窗口,是另一个窗口的孩子。如果在按下某些键时选择这样做,它可以通过该窗口向其父窗口发送消息。

为了标准化对话框中控件之间的导航,Windows还提供了对话管理器'。在本机代码中,对于模态对话框,这由DialogBox函数内的模态消息循环处理。对于无模式对话框,您必须在自己的消息循环中调用IsDialogMessage。这就是它窃取Tab和光标键以在控件之间导航的方式,以及Enter来按下默认按钮。这具有相反的效果,即不允许控件默认处理Enter,多行编辑控件通常会处理。为了发现控件是否想要处理键,对话管理器代码向聚焦控件发送WM_GETDLGCODE消息;如果控件正确响应,则对话管理器返回FALSE,允许DispatchMessage实际将其传递给窗口过程,否则对话管理器会自行执行。

Windows Forms主要是包装旧的本机控件,因此它必须符合Win32的事件模型。它实现了相同的对话管理器方法 - 因此默认情况下,它不允许您查看Tab,Return和光标键。

如果您要处理其中一个密钥,建议的方法是覆盖PreviewKeyDown并将PreviewKeyDownEventArgs IsInputKey属性设置为true

答案 3 :(得分:1)

一个简单而琐碎的但实用的原因:

在类似《太空侵略者》 https://www.mooict.com/c-tutorial-create-a-full-space-invaders-game-using-visual-studio/的游戏中,用户反复敲打空格键以蒸发外星人。当最后一个入侵者消失后,会弹出一个文本框,说“好工作”。用户仍在抽动拇指,打动空格键(或者只是释放键盘缓冲区?),祝贺MessageBox消失了,然后才能被读取。由于Forms如何处理按钮/空格键单击,因此无法解决。

我的自定义对话框使用keypreview来预处理发送到GameOverDialog的击键,以忽略任何空格键的点击。用户必须通过单击鼠标或Enter来关闭。 这只是一个带有“您赢了”标签和[确定]按钮的FixedDialog。

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

另外,我还没有测试过一个有趣的选项:如果您考虑一下,这将是一种将作弊,隐藏消息等嵌入无害的[OK]对话框或任何允许按键预览的表格的好方法。