WPF Input Validation with the Enterprise Library

This shows how to validate user input using the Validation Application Block of the Enterprise Library 4.1 – October 2008 release.

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.

By Michael Detras   Popularity  (10920 Views)
Biography - Michael Detras
.NET developer. Interested in WPF, Silverlight, and XNA.
My blog