Introduction
The following is one way of validating user input. The user fills up a form and submits
the information. The application checks if the submitted information are valid
(the required fields have been filled up and the field values have the correct
format, among others). If at least one field value is invalid, the application
notifies the user through a message box.
Another way is by validating the input before the user submits it. The application
can notify the user of the error by changing a property of the control that contains
the incorrect field value. For example, the background of a text box is set to
red. This method is used in this article.
The Enterprise Library is used to aid in validation. It contains reusable components
that address common problems like logging, exception handling, data access, and
validation. Please check this CodePlex site for more information about the Enterprise Library. The installer can also be downloaded
there.
Getting Started
To illustrate how to use the Enterprise Library for validation, let’s create a simple
WPF application that accepts user input and has a save button that is enabled
or disabled depending on whether the user’s input is correct. The save button
does not do anything when clicked, just to simplify things.
Figure 1. Customer View
I’ll be using data binding so I might as well use the Model-View-ViewModel design
pattern but I’ll make it simple. Basically, we need to define a class, the Model,
having properties that correspond to the fields on the user interface. The field
values are then bounded to these properties. For our example, let’s define the
Customer class.
public class Customer
{
public string Name { get; set; }
public DateTime Birthdate { get; set; }
public string Address { get; set; }
public string Email { get; set; }
}
Listing 1. Customer Class
Next, we need to create our ViewModel class, which will contain the Model object.
Let’s call the class CustomerViewModel. This class also contains a property of type ICommand which is used for saving the
field values. Note that this example uses commands instead of implementing handlers
for events like the Click event. To cut the story short, the save button’s Command
property is bounded to the ICommand object. The following listing shows the CustomerViewModel.
public class CustomerViewModel
{
private Customer customer;
private RelayCommand saveCommand;
public Customer Customer
{
get
{
return customer == null ? customer = new Customer() : customer;
}
}
public RelayCommand SaveCommand
{
get
{
return saveCommand == null ? saveCommand = new RelayCommand(SaveExecute, CanSaveExecute) : saveCommand;
}
}
public void SaveExecute(object parameter)
{
// Do nothing.
}
public bool CanSaveExecute(object parameter)
{
return true;
}
}
Listing 2. CustomerViewModel Class
I got the RelayCommand implementation from one of Josh Smith’s article regarding
MVVM. It comes in very handy. Going back, the DataContext of the window in Figure
1 is assigned to the instance of a CustomerViewModel to enable data binding.
Clicking on the save button executes the SaveExecute method. The CanSaveExecute
method is also executed to determine whether the save button is enabled. Right
now, the save button is always enabled because we haven’t implemented validation
yet.
Adding Rules
Let’s determine the rules that we need to set for every field value. For example,
the name of the customer must be at least 3 characters, the birth date not greater
than the current date and at least of the year 1920s, the address at least 5
characters long and the e-mail should be valid. To help us in this is the Enterprise
Library Validation Application Block. I assume that you have already downloaded
and installed the Enterprise Library.
The Enterprise Library contains several Validator classes. There are the StringLenghtValidator,
DateTimeRangeValidator and NotNullValidator classes, to name a few. You can check
the Enterprise Library documentation to check out the other Validator classes.
In our example, we’ll be using the equivalent Attribute classes so that we can
just decorate the Customer properties. Using these Attribute objects is more
readable than creating Validator objects to validate the properties. To use these
validation classes, we need to add a reference to the Microsoft.Practices.EnterpriseLibrary.Validation
assembly. The following code shows the updated Customer class.
public class Customer
{
[StringLengthValidator(3, 50)]
public string Name { get; set; }
[DateTimeRangeValidator("1920-01-01", "2005-01-01")]
public DateTime Birthdate { get; set; }
[StringLengthValidator(5, 200)]
public string Address { get; set; }
[RegexValidator
(
@"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
+ @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
+ @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
+ @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$"
)]
public string Email { get; set; }
}
Listing 3. Customer Class with Validators
The attributes are quite self-explanatory. For validating the e-mail, I used the
RegexValidator attribute. I got the regular expression from a CodeProject article by Mykola Dobrochynskyy. Most of the Validator classes in the Enterprise Library
are quite sufficient for most validation needs. However, you can create your
own custom Validator by deriving from the Validator class and overriding the
DoValidate method. Okay, the Validator attributes have been set up but we still
don’t know how to notify the user about the incorrect input.
Notifying the User Interface
To notify the user interface of any error, the Customer class should implement the
IDataErrorInfo interface. According to the MSDN Library, the IDataErrorInfo provides
the functionality to offer custom error information that a user interface can
bind to. The following code shows the implemented IDataErrorInfo members.
#region IDataErrorInfo Members
public string Error
{
get
{
StringBuilder error = new StringBuilder();
ValidationResults results = Validation.ValidateFromAttributes<Customer>(this);
foreach (ValidationResult result in results)
{
error.AppendLine(result.Message);
}
return error.ToString();
}
}
public string this[string columnName]
{
get
{
ValidationResults results = Validation.ValidateFromAttributes<Customer>(this);
foreach (ValidationResult result in results)
{
if (result.Key == columnName)
{
return result.Message;
}
}
return string.Empty;
}
}
#endregion
Listing 4. IDataErrorInfo Members
It is great that by simply implementing the IDataErrorInfo interface, the user may
be notified of any data error. However, the validation process is disabled by
default. This can be enabled by setting the ValidatesOnDataErrors property of
the binding you want to validate to true. The following figure shows the window
with validation.
Figure 2. Customer View with Validation
Changing the Validation Error Template
The red borders you see are due to the default behavior of the controls when there
are validation errors. I believe that this behavior is not what most of us want.
At the very least, the error message should be displayed. We can override this
behavior by setting the Validation.ErrorTemplate attached dependency property
on the controls. This property is of type ControlTemplate. The following code
shows our own template.
<ControlTemplate x:Key="ValidationErrorTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Red" FontSize="24" Text="*" ToolTip="{Binding [0].ErrorContent}"/>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
Listing 5. Validation Error Template
This control template is applied to the controls by using styles. Basically, what
the code does is add an asterisk (*) to the left side of the control if there
is a validation error. It does not affect the current layout of the controls
in the window because it is done on another layer, called the adorner layer.
An error message is shown as a tooltip when the mouse hovers above an asterisk.
If you want the error message to be visible below the control, you’ll have to
consider the amount of space it will take up because the other controls won’t
adjust to it. To work around this, you can create a collapsible text block under
the control that is shown when there is an error. The following figure shows
the window with the updated template.
Figure 3. Updated Validation Error Template
Miscellaneous
As you might have noticed in the last figure, the error message is quite technical
and says that the e-mail does not match the regular expression. It would be better
to just say that the e-mail address is invalid. To do this, we’ll just have to
set the MessageTemplate property of the RegexValidator in the Customer class
to “The e-mail address is invalid.”
Also, when we try to correct the field values, the asterisk isn’t removed immediately.
We have to move the focus to other controls first. That is because the UpdateSourceTrigger
properties of some bindings have their default values set to LostFocus. So we
just need to set the value to PropertyChanged.
Lastly, the save button is still enabled when there are errors. The solution to this
is easy. We just need to specify a condition at the CanSaveExecute method. If
the Customer object’s Error property is not equal to an empty string then the
method returns false, thus disabling the control.
Using the Configuration File
What if we need to change the validation rules at some point in time? For example,
we now want to restrict the customer’s name to at least 10 characters. Modifying
and recompiling the source code is not a very good option. Fortunately, the Enterprise
Library lets us use configuration files to set the validation rules. To do this,
click on Enterprise Library Configuration item from the start menu. Then, create
a new application configuration file as shown below.
Figure 4. The Enterprise Library Configuration Editor
Afterwards, right click on the Application Configuration tree item and select New
> Validation Application Block. This will create a Validation Application
Block tree item under Application Configuration. Now, right click on the Validation
Application Block tree item and choose New > Type. This will open a dialog
where you will choose the type of object that you want to validate.
Figure 5. Enterprise Library Type Selector
Since our assembly is not registered in the Global Assembly Cache (GAC), we can directly
load it from file, assuming the project has been built. Click on Load from File…
and locate and select the assembly containing the type definition. The assembly
will be added to the list and the classes will be shown under it. Select the
type we need to validate. This will be added under the Validation Application
Block tree item in the main window. In our example, right click on the Customer
type and select New > Rule Set. This will create the Rule Set tree item under
Customer. Right click on it and select New > Choose Members. The Member Selector
dialog will open.
Figure 6. Enterprise Library Member Selector
Choose the members we want to validate. In our case, those are Name, Birthdate, Address
and Email. After selecting, these properties will show up under the Rule Set
tree item. Select a property and add a Validator like the one shown below.
Figure 7. Selecting a Validator
The Validator you selected will be shown under the property name in the tree. Select
the Validator and set its properties found on the panel on the right. For example,
set the LowerBound and UpperBound properties of the StringLengthValidator of
the Name property to 3 and 50, respectively. You can move all the validation
rules from code to the configuration file if you want, but I would suggest that
you only move those that will probably change in the future. After setting up
the validation rules, save the configuration file as app.config on the same directory
as the application’s project file. Add the app.config file to our project. Lastly
we need to use Validation.ValidateFromConfiguration method in our IDataErrorInfo
members.
ValidationResults results = Validation.ValidateFromConfiguration<Customer>(this, "Rule Set");
If you want to use rules from both attributes and configuration, you can use the
Validation.Validate method. That’s about it. The Visual Studio 2008 solution
using attribute-based rules can be downloaded here. The other, using the configuration file, can be downloaded here.