Tuesday, March 20, 2007

Enterprise WPF: Validation

I am developing an enterprise application with WPF.

WPF is great, but it is very new and not many enterprise applications have been developed with WPF so far. Several things that are part of most enterprise applications are not very well supported with WPF, and you need to put in some extra effort.

One example is validation. Validation happens when the user is entering data into a form, and the application has to validate the user input before accepting it. Some text fields can have only a certain length, a date field has to contain a valid date, the start date cannot be later than the end date, and so forth.

How are developers implementing validation? For example in ASP.NET, there are several validation controls. The ASP.NET developer puts these validation controls on the page together with the textboxes and other controls for the data input. A CompareValidator to check that the start date is before the end date, a RegularExpressionValidator to check for a valid email address, etc.

Is this a good technology? Yes, because many enterprise applications are implemented that way, and they work well. But no, because your validation code is mixed into your user interface layer. In an enterprise application you would like to have your validation logic in your business layer. This is the better place for several reasons:

  • In a distributed application (web, smart client, or client server), you cannot trust your user interface layer and have to perform the validation again on the server, duplicating your validation logic.
  • You may have to support more than one user interface, e.g. a web and a smart client interface. While you can reuse the validation logic in the business layer, you have to develop another validation for each UI.
  • You may have to provide different forms for entering the same type of data, e.g. a customer address and an employee address. Again more versions of the same validation.

With potentially many versions of the validation logic, every change in requirements would become a maintenance challenge.

In comes Enterprise Library 3.0 and the Validation Application Block (VAB), to the rescue. The VAB lets you define validation rules on your business objects using configuration and attributes.

Here is a simple example from the VAB quick start, that shows how to define a simple validation rule on a FirstName property, that validates the length of the string:

        [StringLengthValidator(1, 50, Ruleset = "RuleSetA", MessageTemplate = "Last Name must be between 1 and 50 characters")]

        public string LastName

        {

            get { return lastName; }

            set { lastName = value; }

        }

VAB includes a whole range of validators. To avoid duplication of the validation logic, VAB comes with integrations into Windows Forms, ASP.NET, and WCF. Unfortunately, WPF integration is missing. I also do not like the ASP.NET integration, but that would be another blog post.

It is rather unfortunate that WPF integration is missing from the VAB, because WPF's validation support is limited. The basis for a validation framework exists in WPF, and it is actually very flexible. WPF validation is based on DataBinding - a Binding can have ValidationRules associated with it. Unfortunately there is only one ValidationRule included with WPF at this time. This is illustrated in a great article on CodeProject by Paul Stovell.

In this article Paul builds a WPF validation framework on top of the IDataErrorInfo interface. This interface lets you define validation logic on your business objects. The validation logic is defined procedural, though, not declarative. You can see an example for this in Paul's article.

I have now implemented a WPF integration of the VAB based on a modification of Paul's ErrorProvider. The ErrorProvider lets you wrap a part of the user interface that is based on the same DataContext. The ErrorProvider automatically adds an EnterpriseValidationRule to all data bindings. The EnterpriseValidationRule uses the validation rules defined using VAB on the property that is bound to.

Here is a XAML version of the VAB quickstart:

    <my:ErrorProvider x:Name="epCustomer" RulesetName="RuleSetA">

      <Grid Margin="5">

        <Grid.ColumnDefinitions>

          <ColumnDefinition />

          <ColumnDefinition />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

          <RowDefinition />

          <RowDefinition />

          <RowDefinition />

          <RowDefinition />

          <RowDefinition />

        </Grid.RowDefinitions>

        <Label>First Name:</Label>

        <TextBox Grid.Column="1" Text="{Binding Path=FirstName}"/>

        <Label Grid.Row="1">Last Name:</Label>

        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>

        <Label Grid.Row="2">Date Of Birth:</Label>

        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=DateOfBirth}"/>

        <Label Grid.Row="3">Email:</Label>

        <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Email}"/>

        <Label Grid.Row="4">Rewards Points:</Label>

        <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Path=RewardPoints}"/>

      </Grid>

    </my:ErrorProvider>

Note that all that is added to the XAML code is the ErrorProvider, and we are getting automatic validation based on the validation rules of the bound business objects.

5 comments:

Sergey said...

Could you show please, how did you modified Paul's ErrorProvider.

Anonymous said...

Hi Martin,

First off, great work! The block doesn't however handle all bindings correctly. e.g. if you have a label element within your block wrapped by an errorprovider and the label has a Target property which is bound to an ElementName, the block will throw an exception. Also, the block seems to validate right at the start of loading the XAML. Is this behaviour which is expected? You can contact me at martijn.hoogendoorn[microsoft].

elise said...

Hi,
I was wondering in your sample... You show the first option, where the validation is declared with tags by code (like the LastName code you show here).
But what if we want to use the second VAB option ? (meaning, the .dll.config, taking advantage for example of the enterprise library configuration visual tool), how can you use it under WPF ?
I have used your Validation.Integration.WPF project, and in the XAML sample, there is a call to RulesetName="RuleSetA" to use the RuleSet defined by code. How can i call the RuleSetB defined in the config file of the ValidationQuickStart.BusinessEntities project ? Is it doable ?

GreatTall1 said...
This comment has been removed by the author.
GreatTall1 said...

Thanks for the sample code, this integration is really slick!

A PropertyComparisonValidator won't work with the WPF integration because of the PropertyMappedValidatorValueAccess class that is being used to read the property values. On a PropertyComparisonValidator, the property you are comparing to is not the property that the validator is associated with. This is being checked in the GetValue() method, and returns the 'The property name "{0}" is not mapped to validators in the naming context' error message.

I found this blog post that talks a little bit more about the ValueAccess class, but it's a little convoluted.

http://solepano.blogspot.com/2007/05/developing-custom-property-comparison.html