如何防止控件在模式窗体打开时捕获 KeyDown 事件?

How to prevent controls from capturing KeyDown events while a modal form is open?

我有一个带有 CancelButton 和 AcceptButton 的表单(名为 btnCancel 和 btnOK)。我有一些组合框作为输入字段。

组合框阻止我的 AcceptButton 和 CancelButton 接收 Escape 和 Enter 键,因此我将这段代码添加到所有字段的 KeyDown 事件中:

if (e.KeyData == Keys.Escape)
{
    ComboBox field = (ComboBox)sender;
    if ((field.DropDownStyle == ComboBoxStyle.Simple) || (!field.DroppedDown))
    {
        e.SuppressKeyPress = true;
        btnCancel.PerformClick();
    }
}
else if (e.KeyData == Keys.Enter)
{
    ComboBox field = (ComboBox)sender;
    if ((field.DropDownStyle == ComboBoxStyle.Simple) || (!field.DroppedDown))
    {
        e.SuppressKeyPress = true;
        btnOK.PerformClick();
    }
}

这是确定按钮的点击事件中的代码:

if (!changesAreSaved)
{
    SaveChangesToNode();
}

List<int> invalidIndices = ValidateAndRefineNodes(true);

if (invalidIndices.Count == 0)
{
    this.DialogResult = DialogResult.OK;
    this.Close();
}
else
{
    MessageBox.Show(this, "Enter correct values for all fields before you press OK.", "Cannot Save Information",
        MessageBoxButtons.OK, MessageBoxIcon.Error);
}

一切正常,但是当 ComboBox 具有焦点并且我按下键盘上的 Enter 键时,btnOK_Clicked 再次调用 Fields_KeyDown 仅当 显示其MessageBox(在 if 的 else 部分)。就在调用 MessageBox.Show(...) 之后,第二次无故调用 KeyDown 事件。

这是第一次调用的调用堆栈:

这是第二个:

第二次调用根本不应该发生。在第二个调用堆栈中,第一个 btnOK_Click(第三行)再次从 MessageBox.Show(...) 调用 Fields_KeyDown(第二行)。这怎么可能?我很困惑...

第二次调用的调用堆栈,外部代码可见:

虽然我不知道这种行为背后的主要原因。

但在这种情况下显然 KeyDown 事件触发了 2 次。 (设置一个断点,你会看到。)

由于您需要在代码中处理它,您可以尝试忽略 Enter 键之一:

bool handled = true;
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        /*Prevent handling the Enter key twice*/
        handled = !handled;
        if(handled)
            return;

        //Rest of logic
        //OkButton.PerformClick();
    }
}

您无法正确处理 KeyDown 事件中的 Escape 和 Enter 键,因为它们是在键盘预处理阶段处理的 - Control.IsInputKey and Control.ProcessDialogKey。通常控件会为您执行此操作,但当 DropDownStyleSimple 时,ComboBox 实现中似乎存在错误。

要获得所需的行为,请像这样创建和使用您自己的 ComboBox 子类

public class MyComboBox : ComboBox
{
    protected override bool IsInputKey(Keys keyData)
    {
        if (DropDownStyle == ComboBoxStyle.Simple)
        {
            switch (keyData & (Keys.KeyCode | Keys.Alt))
            {
                case Keys.Return:
                case Keys.Escape:
                    return false;
            }
        }
        return base.IsInputKey(keyData);
    }
}

P.S。当然不要忘记删除您的 KeyDown 事件处理程序。