如何在Windows Forms应用程序中实现控制验证?
我正在尝试更好地了解验证在Windows窗体应用程序中的工作原理。互联网充满了微不足道的例子,但是我找不到一个解释控制验证的非平凡的例子。无论如何,感谢 SwDevMan81 和汉斯·帕斯特(Hans Passant)我从一个比昨天好的地方开始。
I am trying to better understand how validation works in a Windows Forms application. The internets are full of trivial examples, but I couldn't find a single non-trivial example explaining control validation. Anyway, thanks to SwDevMan81 and Hans Passant I am starting from a much better place than yesterday.
真实应用程序具有许多TextBox控件的对话框。每个控件都实现了验证事件。如您在示例中所看到的, ValidateChildren 被作为结果的点击事件导致验证事件发送到每个控件。该应用程序还使用 ErrorProvider 控件给用户反馈。昨天,我不明白如何使用Ok按钮单击事件来执行此验证。今天,我的对话按预期方式工作。单击确定按钮会导致ErrorProvider执行控件无效并且对话框未意外关闭的事情。
The "real application" has a dialog with many TextBox controls. Each of the controls implements the Validating event. As you can see in the example, ValidateChildren is called as a result of the Click event causing the Validating event to be sent to each of the controls. The app also uses an ErrorProvider control to give the user feedback. Yesterday, I did not understand how to use the Ok button Click event to perform this validation. Today, my dialog works as expected. Clicking the Ok button causes the ErrorProvider to do it's thing where a control is not valid and the dialog is not closing unexpectedly.
所以当这似乎工作时,我离开感觉到我在线外彩。在Windows Forms应用程序中是否有一个最佳实践文档/控件验证站点?
So while this seems to work, I am left feeling that that I "colored outside of the lines". Is there a "best practice" document/site for control validation in a Windows Forms application?
在许多仍然令我困惑的事情中,我无法找到说明对话框的行为,当Ok按钮 DialogResult 属性设置为返回 DialogResult .OK 。为什么设置此属性会干扰验证? (尝试我的例子,没有这一行,看看我的意思。)
Of the many things that are still confusing me, I am unable to find an explanation for the behavior of my dialog when the Ok button DialogResult property is set to return DialogResult.OK. Why does setting this property interfere with validation? (Try my example with and without that line, to see what I mean.)
我昨天的问题(会出现)主要来自于不了解ValidateChildren方法和从我的设置将按钮DialogResult属性设置为DialogResult.OK。将此属性设置为DialogResult.None似乎会更改Form类的一些自动行为。
My problems from yesterday (it would appear) stem mostly from not understanding the ValidateChildren method and from my setting the Ok button DialogResult property to DialogResult.OK. Setting this property to DialogResult.None seems to change some automatic behavior of the Form class.
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;
}
}
}
编辑:解。我认为它很容易使用,同时也满足所有其他要求。我提前道歉这个问题多久了。如果我可以告诉你所有真正的应用程序,那么为什么这么重要呢会更有意义。无论如何,感谢帮助这只老狗学习一个新的技巧。
here is the final solution. I think it is easy to use while also fulfilling all the other requirements. I apologize in advance for how long this question ended up being. If I could show you all the real application, it would make more sense as to why this is so important. Anyway, thanks for helping this old dog learn a new trick.
答案是为需要验证的每个控件创建一个ErrorProvider(与整个对话框的一个ErrorProvider相比)。之后,这一切都很简单。
The answer was to create one ErrorProvider for each control needing validation (vs. one ErrorProvider for the whole dialog. After that, it all was pretty simple.
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; } }
}
}
这个问题是后续的一个我昨天问的一个。
This question is a follow up to one that I asked yesterday.
分配 DialogResult
属性是关闭对话框。当它设置为无
时,它保持运行。您不需要关闭()
调用。调用 ShowDialog()
的代码获取您分配为返回值的 DialogResult
值。所以它知道对话框是否已经关闭,或者刚刚取消。
Assigning the DialogResult
property is what makes a dialog close. It keeps running while it is set to None
. You don't need the Close()
call. The code that calls ShowDialog()
gets the DialogResult
value you assigned as the return value. So it knows whether the dialog was closed with OK or just canceled.
另请注意,您编写验证事件处理程序的方式不需要 ValidateChildren()
。您设置 e.Cancel = true
以防止用户离开文本框。这意味着当文本框被验证为可以时,她只能访问OK按钮。然而,您必须确保在显示对话框时首先选择具有验证的控件。
Also note that the way you wrote the validation event handler, you don't need ValidateChildren()
. You set e.Cancel = true
to prevent the user from moving away from the text box. Which means that she can only get to the OK button when the text box was validated to be okay. You do however have to make sure that a control that has validation is selected first when the dialog is shown.
友好的对话框是用户可以自由选择控制和挑选容易的。您现在需要两个验证,一个检查输入的值是否有效,另一个检查是否没有缺少值。您可以通过在验证事件处理程序中接受一个空字符串来获取此信息。但是Winforms不支持后者,您需要代码。
A friendly dialog is one where the user is free to tab between controls and pick off the 'easy ones'. You now need two validations, one that checks if the entered value is valid, another one that checks if there are no missing values. You get this by accepting an empty string in the Validation event handler. But the latter is not well supported by Winforms, you need code.