如何在Windows窗体应用程序中实现控件validation?

我试图更好地了解validation如何在Windows窗体应用程序中工作。 互联网上充满了琐碎的例子,但我找不到一个解释控制validation的非平凡的例子。 无论如何,多亏了SwDevMan81和Hans Passant,我从一个比昨天更好的地方开始。

“真正的应用程序”有一个包含许多TextBox控件的对话框。 每个控件都实现Validating事件。 正如您在示例中所看到的, ValidateChildren作为Click事件的结果被调用,导致Validating事件被发送到每个控件。 该应用程序还使用ErrorProvider控件来提供用户反馈。 昨天,我不明白如何使用Ok按钮Click事件来执行此validation。 今天,我的对话框按预期工作。 单击“确定”按钮会导致ErrorProvider执行控件无效且对话框未意外关闭的操作。

因此,虽然这似乎有效,但我感觉我“在线外”。 在Windows窗体应用程序中是否有用于控制validation的“最佳实践”文档/站点?

在许多令我困惑的事情中,当Ok按钮DialogResult属性设置为返回DialogResult.OK时,我无法找到对话框行为的解释。 为什么设置此属性会干扰validation? (尝试使用和不使用该行的示例,看看我的意思。)

我昨天的问题(看起来似乎)主要源于不了解ValidateChildren方法以及我将Ok按钮DialogResult属性设置为DialogResult.OK。 将此属性设置为DialogResult.None似乎更改了Form类的一些自动行为。

TIA

using System; using System.ComponentModel; using System.Windows.Forms; namespace ConsoleApp { class Program { static void Main( string[] args ) { Dialog dialog = new Dialog(); if( dialog.ShowDialog() == DialogResult.OK ) Console.Beep(); } } public class Dialog : Form { TextBox m_TextBox0; TextBox m_TextBox1; // not validated TextBox m_TextBox2; Button m_OkBtn; Button m_CancelBtn; ErrorProvider m_ErrorProvider; public Dialog() { m_TextBox0 = CreateTextBox( 0, "TextBox 0" ); m_TextBox1 = CreateTextBox( 1, "TextBox 1" ); m_TextBox2 = CreateTextBox( 2, "TextBox 2" ); m_OkBtn = CreateButton( 3, "Ok" ); m_CancelBtn = CreateButton( 4, "Cancel" ); m_ErrorProvider = new ErrorProvider( this ); //m_BtnOk.DialogResult = DialogResult.OK; m_OkBtn.Click += new EventHandler( BtnOk_Click ); m_OkBtn.CausesValidation = true; m_CancelBtn.DialogResult = DialogResult.Cancel; m_CancelBtn.CausesValidation = false; } void BtnOk_Click( object sender, EventArgs e ) { if( ValidateChildren() ) { DialogResult = DialogResult.OK; Close(); } } void TextBox_Validating( object sender, CancelEventArgs e ) { m_ErrorProvider.Clear(); TextBox textBox = sender as TextBox; // m_TextBox1 is always valid, the others are valid if they have text. bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0; if( !valid ) m_ErrorProvider.SetError( textBox, "Error " + textBox.Name ); e.Cancel = !valid; } Button CreateButton( int index, string name ) { Button button = new Button(); button.TabIndex = index; button.Text = name; button.Location = new System.Drawing.Point( 0, index * 30 ); Controls.Add( button ); return button; } TextBox CreateTextBox( int index, string name ) { Label label = new Label(); label.Text = name; label.Location = new System.Drawing.Point( 0, index * 30 ); TextBox textBox = new TextBox(); textBox.TabIndex = index; textBox.CausesValidation = true; textBox.Validating += new CancelEventHandler( TextBox_Validating ); textBox.Location = new System.Drawing.Point( 100, index * 30 ); Controls.Add( label ); Controls.Add( textBox ); return textBox; } } } 

编辑:这是最终的解决方案。 我认为它很容易使用,同时也满足所有其他要求。 我提前为这个问题结束多久而道歉。 如果我可以向您展示所有真正的应用程序,那么为什么这么重要就更有意义了。 无论如何,感谢帮助这只老狗学习新技巧。

答案是为每个需要validation的控件创建一个ErrorProvider(相对于整个对话框的一个ErrorProvider。之后,一切都非常简单。

 using System; using System.ComponentModel; using System.Windows.Forms; namespace ConsoleApp { class Program { static void Main( string[] args ) { Dialog dialog = new Dialog(); if( dialog.ShowDialog() == DialogResult.OK ) Console.Beep(); } } public class CompositeControl { Label m_Label; TextBox m_TextBox; ErrorProvider m_ErrorProvider; Dialog m_Dialog; public CompositeControl( int index, string name, Dialog dialog ) { m_Label = new Label(); m_Label.Text = name; m_Label.Location = new System.Drawing.Point( 0, index * 30 ); m_TextBox = new TextBox(); m_TextBox.TabIndex = index; m_TextBox.CausesValidation = true; m_TextBox.Validating += new CancelEventHandler( TextBox_Validating ); m_TextBox.Location = new System.Drawing.Point( 100, index * 30 ); m_Dialog = dialog; m_ErrorProvider = new ErrorProvider( m_Dialog ); m_Dialog.Controls.Add( m_Label ); m_Dialog.Controls.Add( m_TextBox ); } void TextBox_Validating( object sender, CancelEventArgs e ) { TextBox textBox = sender as TextBox; if( !m_Dialog.IsClosing && textBox.Text.Length == 0 ) return; // m_TextBox1 is always valid, the others are valid if they have text. bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0; if( !valid ) m_ErrorProvider.SetError( textBox, "Error " + textBox.Name ); else m_ErrorProvider.Clear(); e.Cancel = !valid; } } public class Dialog : Form { CompositeControl m_CompositeControl0; CompositeControl m_CompositeControl1; // not validated CompositeControl m_CompositeControl2; Button m_OkBtn; Button m_CancelBtn; bool m_IsClosing = false; public Dialog() { m_CompositeControl0 = new CompositeControl( 0, "TextBox 0", this ); m_CompositeControl1 = new CompositeControl( 1, "TextBox 1", this ); m_CompositeControl2 = new CompositeControl( 2, "TextBox 2", this ); m_OkBtn = CreateButton( 3, "Ok" ); m_CancelBtn = CreateButton( 4, "Cancel" ); //m_BtnOk.DialogResult = DialogResult.OK; m_OkBtn.Click += new EventHandler( BtnOk_Click ); m_OkBtn.CausesValidation = true; m_CancelBtn.DialogResult = DialogResult.Cancel; m_CancelBtn.CausesValidation = false; } void BtnOk_Click( object sender, EventArgs e ) { m_IsClosing = true; if( ValidateChildren() ) { DialogResult = DialogResult.OK; Close(); } m_IsClosing = false; } Button CreateButton( int index, string name ) { Button button = new Button(); button.TabIndex = index; button.Text = name; button.Location = new System.Drawing.Point( 0, index * 30 ); Controls.Add( button ); return button; } public bool IsClosing { get { return m_IsClosing; } } } } 

这个问题是我昨天问过的问题的后续问题。

分配DialogResult属性是关闭对话框的原因。 它在设置为None保持运行。 您不需要Close()调用。 调用ShowDialog()的代码获取您指定为返回值的DialogResult值。 所以它知道对话框是用OK关闭还是刚刚取消。

另请注意,编写validation事件处理程序的方式不需要ValidateChildren() 。 您设置e.Cancel = true以防止用户离开文本框。 这意味着当文本框被validation为正常时,她只能进入“确定”按钮。 但是,您必须确保在显示对话框时首先选择具有validation的控件。

一个友好的对话框是用户可以在控件之间自由选项并选择“简单”的对话框。 您现在需要两个validation,一个validation输入的值是否有效,另一个validation是否没有丢失值。 您可以通过在Validation事件处理程序中接受空字符串来获得此结果。 但Winforms不能很好地支持后者,你需要代码。

我知道这有点晚了,但我会再向Hans回答一下。 创建文本框事件validation并将m_ErrorProvider.Clear()移动到Validated事件。 validation完成时(e.cancel == false),将触发validation的事件。 所以你会有这样的事情:

 void TextBox_Validating( object sender, CancelEventArgs e ) { TextBox textBox = sender as TextBox; bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0; if( !valid ) m_ErrorProvider.SetError( textBox, "Error " + textBox.Name ); e.Cancel = !valid; } private void TextBox_Validated(object sender, System.EventArgs e) { TextBox textBox = sender as TextBox; m_ErrorProvider.SetError(textBox, ""); }