WinForms验证事件阻止Escape键关闭表单

时间:2011-01-25 23:06:35

标签: c# .net winforms validation

我有一个带有单个TextBox的简单表单,以及OK和Cancel按钮。 Form的AcceptButton和CancelButton设置正确,OK和Cancel按钮的DialogResult设置为'OK'和'Cancel'。

我想在TextBox中添加验证,这会阻止用户在验证失败时对表单进行确认,但也会允许他们像往常一样取消。

默认情况下,在所有控件上,CausesValidation属性为True,但我在取消按钮上将其更改为False。

果然,单击“确定”或按Enter键将运行我连接到TextBox的验证事件。按取消按钮可以绕过验证,这是完美的。

但是,按Escape取消表单执行与按下取消按钮相同的操作 - 它会引发验证事件并阻止用户退出。

是否有任何方法可以使Escape键按预期执行,即不会引发Validating事件,就像按下Cancel按钮一样?

完整有效的解决方案是:

创建一个新的Windows窗体应用程序。将第二个表单添加到项目中。

在InitializeComponent()之后将此代码粘贴到Form1的构造函数中:

MessageBox.Show((new Form2()).ShowDialog().ToString());

这表示DialogResult从我们的第二个表单传回来。

在InitializeComponent()之后将此代码粘贴到Form2的构造函数中:

TextBox txtName = new TextBox();

txtName.Validating +=
    new CancelEventHandler((sender, e) =>
    {
        if (txtName.Text.Length == 3)
        {
            MessageBox.Show("Validation failed.");
            e.Cancel = true;
        }
    });

Button btnOk = new Button
{
    Text = "OK",
    DialogResult = DialogResult.OK
};
Button btnCancel = new Button
{
    Text = "Cancel",
    CausesValidation = false,
    DialogResult = DialogResult.Cancel
};
FlowLayoutPanel panel = new FlowLayoutPanel();
panel.Controls.AddRange(new Control[] 
{
    txtName, btnOk, btnCancel 
});

this.AcceptButton = btnOk;
this.CancelButton = btnCancel;

this.Controls.Add(panel);

在此简化示例中,如果输入了3个字符,则文本框将不允许您继续。即使有3个字符,您也可以直接按取消按钮或关闭表格;然而,按下Escape键将执行相同的操作 - 它会触发Validating事件,而它应该与按Cancel取消相同。

4 个答案:

答案 0 :(得分:16)

是的,这是ValidateChildren方法的一个尴尬的怪癖。它不知道取消是有意的。粘贴此代码以解决问题:

    protected override void OnFormClosing(FormClosingEventArgs e) {
        base.OnFormClosing(e);
        e.Cancel = false;
    }

为避免运行导致副作用的Validate事件处理程序(如消息框),请将此语句添加到方法的顶部:

    private void txtName_Validating(object sender, CancelEventArgs e)
    {
        if (this.DialogResult != DialogResult.None) return;
        // etc..
    }

将此代码粘贴到表单中,以便在尝试验证表单之前设置DialogResult:

    protected override bool ProcessDialogKey(Keys keyData) {
        if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
        return base.ProcessDialogKey(keyData);
    }

答案 1 :(得分:6)

我刚看到这个问题,因为我正在寻找相同的解决方案,并且ProcessdialogKey的覆盖是MS批准的解决方案,直到他们修复错误(Escape应该像单击取消一样)。此处也可以找到对此错误的讨论(仅使用Visual Basic而不是C#。错误已超过5年,显然仍未修复):Bug or Feature? CancelButton vs Escape Key我正在尝试解决C ++解决方案。

编辑添加: 来自C#链接的解决方案:

protected override bool ProcessDialogKey(Keys keyData)
{
    if (keyData == Keys.Escape)
    {
        this.AutoValidate = AutoValidate.Disable;
        cancelButton.PerformClick();
        this.AutoValidate = AutoValidate.Inherit;
        return true;
    }
    return base.ProcessDialogKey(keyData);
}

答案 2 :(得分:1)

实际上,这会导致新的问题,因为它拦截了其他控件应该使用的转义,例如:如果你有一个组合框丢弃,按下escape应该关闭组合框而不是退出对话框,上面的代码将会这样做。

可以在转义键事件上豁免某些控件类型,但这不是一个好的解决方案,只是时间问题,直到另一个内部使用转义的控件被引入到表单中,例如,一个扩展控件,其中应该在转义时退出编辑模式。

IMO非常迟钝,他们在逃跑时运行验证。有谁知道背后的想法是什么,因为它不是一个错误...但它是一个错误。

如果该代码调用base.ProcessDialogKey(keyData)而不是cancelButton.PerformClick,那么您将更接近解决方案,因为由其他人决定如何处理转义键。但是在这里将AutoValidate设置为Disabled然后将其恢复为原始值并不会阻止验证,因为它可能只是简单地发布事件并在队列中放置消息,直到它被设置回之后才使用该值它的原始价值。

通过将其设置为“已禁用”并且不将其返回到原始值,它将起作用,但是如果转义键然后被例如拦截。上面提到的组合框丢失了,那么你突然也可以在OK上禁用验证。

德勤!

任何人都有关于如何使其发挥作用的任何其他明智的想法,而不必指定所有可能的控制类型以及它们何时需要逃脱的条件,例如:控制是ComboBox并检查它是否被删除,如果是这样的话。

答案 3 :(得分:0)

ProcessDialogKeys和Validating处理程序的答案都不适用于我,可能是因为我使用的是errorProvider而不是MessageBox。我得到的最简单的答案就是忘记验证并使用以下内容: -

buttonOk.Click += (_,__) =>
{
  if(txtName.Text.Length == 3)
  {
    errorProvider1.SetError(txtName, "Wrong Length!");
    DialogResult = DialogResult.None;
  }
  else
  {
    errorProvider1.SetError(txtName, string.Empty);
  }
};