我有一个带有单个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取消相同。
答案 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);
}
};