Validate user input in Windows Forms






4.71/5 (46 votes)
Apr 26, 2006
6 min read

434348

17124
You needn't write any code for control validation; just set a control's validation information at design-time and reduce maintenance workload.
Introduction
ASP.NET provides a mechanism to validate user input. That is, before a user submit his/her page, there are several kinds of validators that validate the input. However, in WinForms, we can't get those excellent components. When we validate a text in a control, for example, there is a TextBox
named "txtPassword
", and it requires users to input a password text beginning with a letter and having 3-8 length numbers or letters, we must register txtPassword
's Validating
event, and the event method should like:
private void txtPassword_Validating(object sender, CancelEventArgs e)
{
if (string.IsNullOrEmpty(txtPassword.Text))
{
errorProvider1.SetError(txtPassword, "Password required!");
}
else if (!Regex.IsMatch(txtPassword.Text, @"[A-Za-z][A-Za-z0-9]{2,7}"))
{
errorProvider1.SetError(txtPassword, "Password invalid!");
}
else
{
errorProvider1.SetError(txtPassword, null);
}
}
In the Submit button, the code should be like:
private void btnOK_Click(object sender, System.EventArgs e)
{
foreach (Control control in this.Controls)
{
// Set focus on control
control.Focus();
// Validate causes the control's Validating event to be fired,
// if CausesValidation is True
if (!Validate())
{
DialogResult = DialogResult.None;
return;
}
}
}
It's really a dull assignment, and if there is a lot of controls in your form, it makes maintenance difficult. My goal is to validate controls without any code! With Validator
, you drop it to a form and enter the validation rules for a TextBox
or a ComboBox
just like using the ToolTip
component.
Background
I got the afflatus from the following article but implement a different approach. I suggest you read it:
- Extending Windows Forms with a Custom Validation Component Library - Michael Weinhardt.
Using the code
First, create a new Windows Forms application, and follow these steps to use the Validator
component:
- Add assembly
Open the "Toolbox" window, right click in the "General" tab, and click "Choose items...".
If you have strong named Validator assembly and has added it to the GAC, you can choose it in the ".NET Framework components" list. Or else, in the example, click the "Browse" button and locate your Validator.dll assembly.
Now, you can find it in "Toolbox".
- Add and configure the component
Drag the
Validator
which has been added by the first step, and drop it on the form. Its property window should be like:The important properties for this component are:
Form
- specify a form where validated controls lay onto.Validator
will register itsFormClosing
event, validates controls, and decides whether to close the windows.Mode
- thisenum
value can been one, or combined byFocusChange
andSubmit
.FocusChange
means when validating a control fails, the focus stops on that control or is changed to the next tab index control;Submit
means that validate controls while the form is closing, and prevent the form from closing when the validation fails.BlinkRate
,BlinkStyle
,Icon
,RightToLeft
: same asErrorProvider
properties, please use MSDN for help.
- Configure the TextBoxes and their properties
Now we will assume that there are three
TextBox
es and one Submit button on the form.Name Text Properties Function txtName
User name, required input. txtPassword
PasswordChar
= "*"Input password, begins with an alphabet and has 3-8 numbers or alphabets. txtRePassword
PasswordChar
= "*"Re-input password, same as password. txtSubmit
Submit DialogResult
= OKSubmit and close form. Configure validation info for the controls. Now, configure
txtRePassword
for the demo: show thetxtRePassword
property window. You will surprisingly find an extra category, "Validation
", there:Type
: dropdown the "Type on validator1" item and select theRequired
andCompare
items.RequiredMessage
: input - "Please input re-password.".ComparedControl
: dropdown and select "txtPassword
".CompareOperator
: dropdown and select "Equal
".CompareMessage
: input "Re-Password not same as password".
The extended properties should be like the following table (omit message):
Name Validation txtName
Type
=RequiredtxtPassword
Type
=Required|RegularExpression
RegularExpression
= ^[A-Za-Z][A-Za-z0-9]{2,7}$RegularExpressionOptions
=None
txtRePassword
Type
=Required|Compare
ComparedControl
=txtPassword
CompareOperator
=Equal
- Test your form
Congratulations! You got to the point of using the
Validator
. Run your form, and when you enter an invalid value in theTextBox
, it will show an error message. You click the "Submit" button, it will not close the window while there are still errors. For a full demo, I have got a "register" example for you, please analyse it yourself. If you have any problems, feel free to contact me.
Points of Interest
In this section, I want to say more about advanced components programming. It will be a guide to people who are interested in my Validator
component and will show the key points of the Validator
. Here, I will assume you that have basic components programming skills, like creating a component class, applying DefaultValue
, DefaultEvent
, and Browsable
attributes, and so on.
- Extend property
ErrorProvider
,ToolTip
, andValidator
- these components provide a mechanism which extends controls' (or components') properties, in the property window. You will see text like "Type on validator1", which meansType
property is an extended property and is provided by thevalidator1
control (or component). To have this amazing feature:- The component must implement the
IExtenderProvider
interface.public partial class Validator : Component, IExtenderProvider
IExtenderProvider
has an abstract method that must be implements (from MSDN):CanExtend
: Returns whether or not the provider can provide an Extender for the given object in the specified category.
In
Validator
, we validate controls which allow user inputs, likeComboBox
(simple, dropdown),TextBox
,RichTextBox
, thus, theValidator
'sCanExtend
method is:public bool CanExtend(object extendee) { // Only accept TextBoxBase driver class // (TextBox and RichTextBox) and ComboBox if (extendee is TextBoxBase || extendee is ComboBox) { return true; } else { return false; } }
- Apply the
ProvideProperty
attribute and the correlated Get/Set method.ProviderProperty
attribute shouldn't run solely, it must have the Get/Set pair methods in the component class. InValidator
, I define an extended property for the control.[ProvideProperty("Type", typeof(Control)) ...... public partial class Validator : Component, IExtenderProvider
Also, has Get/Set methods:
public partial class Validator : Component, IExtenderProvider { DefaultValue(Itboy.Components.ValidationType.None)] ... ... public ValidationType GetType(Control control) { ... ... } public void SetType(Control control, ValidationType type) { ... ... } }
- The component must implement the
- Editor for the property window
When you edit the
ForeColor
property for aTextBox
in the property window, it drops down a Color Editor dialog.When you edit the Lines property for a
TextBox
in the property window, it shows a String Collection Editor dialog.Actually, a property window only provides a simple text input editor as default. How do Color and Lines properties do this? Using the Reflector v4.2 to disassemble the
System.Drawing.Color struct
in the System.Drawing.dll assembly, we find thisstruct
that applies theEditor
attribute.[Editor("System.Drawing.Design.ColorEditor, ..."]
Disassemble the
System.Windows.Form.TextBoxBase
(TextBoxBase
derives fromTextBox
) class in the System.Windows.Form.dll assembly, and we will find that theLines
property applies theEditor
attribute.[Editor("System.Windows.Forms.Design.StringArrayEditor, ..."]
The
Editor
attribute can be applied to either a class (struct) or a property. If classA
applies theEditor
attrubute, any other class can define properties of class A. Those properties will always be edited by the specified editor in the property window.Only those properties which apply the
Editor
attrubute will be edited by the specified editor; although other property types are same, they will be edited by the default simple text box.In the Validator project, I wrote a
FlagEditor
class, and it can edit theFlag enum
type. Like theRegexOptions
enum
, you can choose the bit value set using | (logic OR), or click the "None
" button to setenum
value to 0.To implement the
Editor
attribute, the class must derive fromUITypeEditor
:public sealed partial class FlagsEditor : UITypeEditor
and override the
EditValue
andGetEditStyle
methods. For details, you can refer to MSDN.My
FlagsEditor
class:public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { ..... // Validate value is a enum type with Flags attribute. if (!value.GetType().IsEnum) { return value; } object[] attributes = value.GetType().GetCustomAttributes(typeof(FlagsAttribute), false); if ((attributes == null) || (attributes.Length == 0)) { return value; } ..... if (service != null) { FlagsEditorControl control = new FlagsEditorControl(value); service.DropDownControl(control); // Create new enum value. Type type = value.GetType(); object newValue = Activator.CreateInstance(type); FieldInfo field = type.GetFields(BindingFlags.Public | BindingFlags.Instance)[0]; field.SetValue(newValue, control.EditValue); } ...... }
First, it asserts that we can only edit the
enum
type withFlags
attribute. Secondly, it drops down a user control which has a "None
" button to set theenum
value to 0, and has several check list items each of which represents one enumeration member except "None
". - Component Designer
Have you noticed that, if you set the focus on a
ListView
control at design time, there appears a triangle button on it. When you click it, it will show a context menu.Besides, when you drop a
ErrorProvider
to aForm
, it will automatically generate a line like:this.errorProvider1.ContainerControl = this;
How does C# do that? In
Validator
, I apply theDesign
attribute:[Designer(typeof(Validator.ValidatorDesigner), typeof(IDesigner))]
ValidatorDesign
derives fromComponentDesign
which provides an approach to above issues.ComponentDesign
has a virtual propertyActionLists
, which returns a menu like theListView
control, and has avirtual
methodInitializeNewComponent
telling the IDE how to generate code.
History
- April 11th, 2006 - First version.
Thanks to
- Michael Weinhardt, for his article in MSDN.
- Bo Shen, who checks my article and corrects my English.