Bài viết về WPF

January 17, 2011 Leave a comment

http://www.arrangeactassert.com/category/wpf/

Advertisements
Categories: Uncategorized

WPF: Validation made easy with IDataErrorInfo

January 17, 2011 Leave a comment

Recently I came across an interface called IDataErrorInfo used for validation, and apparently it’s been around since the early days. I’ve never seen any use of it, or heard of it’s usage until now. It’s been used with WPF validation and integrates very seamlessly too.

  1. Step 1: Create data model with IDataErrorInfo
  2. Step 2: Databind the data to your input fields
  3. Step 3: Customize the Error Template to get different look and feel

WPFValidation

As you can see from the screenshot, validation is triggered when the fields input fail the criteria. The “Add” button on the screen is also disabled/enabled in accordance to the validation via the use of WPF Command System.

Step 1: Creating data model with IDataErrorInfo

To get started with this, you first need a model that implements IDataErrorInfo, like so…

01 public class Customer : IDataErrorInfo
02 {
03 public string FirstName { get; set; }
04 public string LastName { get; set; }
05 public int Age { get; set; }
06
07 #region IDataErrorInfo Members
08
09 public string Error
10 {
11 get { throw new NotImplementedException(); }
12 }
13
14 public string this[string columnName]
15 {
16 get
17 {
18 string result = null;
19 if (columnName == "FirstName")
20 {
21 if (string.IsNullOrEmpty(FirstName))
22 result = "Please enter a First Name";
23 }
24 if (columnName == "LastName")
25 {
26 if (string.IsNullOrEmpty(LastName))
27 result = "Please enter a Last Name";
28 }
29 if (columnName == "Age")
30 {
31 if (Age < = 0 || Age >= 99)
32 result = "Please enter a valid age";
33 }
34 return result;
35 }
36 }
37
38 #endregion
39 }

Looking at the interface implementation, we put in checks for the Property Names we are interested in validating, and return an error message for each one that failed the criteria. For example, we are validating that the first and last names are not empty fields.

Step 2: Databind the data to your input fields

So how does WPF know how to use this interface to validate the input fields? We make use of the coolness of data binding! Below is a snippet of a TextBox with data binding.

1 <textbox x:Name="tbFirstName" Grid.Row="0" Grid.Column="1" Validation.Error="Validation_Error"
2 Text="{Binding UpdateSourceTrigger=LostFocus, Path=FirstName,
3 ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />

UpdateSourceTrigger specifies the condition for updating the source. I have set it to LostFocus instead of PropertyChanged, because PropertyChanged will degrade performance by triggering an update on every keystroke. There also another option called Explicit, meaning you have to trigger the update manually by calling the UpdateSource method.

ValidatesOnDataError is the super star behind all this. By flipping this flag on, the databinding will be able to communicate with any data types that implement IDataErrorInfo. (You might also want to read more about Validation.Error event and NotifyOnValidationError.)

Now that we understand how all these tie together, next question is how to display the error messages. By default, WPF notifies user of a error by highlighting the input field with a red border only. To display the error message or create a whole new look (like how I’ve done it), you need to do some work.

There’s a Validation static class in the WPF framework that’s designed for supporting data validation. You can attach it’s properties and event to any UIElement, such as the Validation.Errors property which holds a collection of error messages. We can now make use of this property to display our error message, for example in a ToolTip when user mouse overs the error icon.

Step 3: Customize the Error Template to get different look and feel

In WPF you can customize almost anything on the UI which is awesome. In order to customize our look and feel of the error, we change the ErrorTemplate of our textbox by defining a new style.

01 <style TargetType="{x:Type TextBox}">
02 <setter Property="VerticalAlignment" Value="Center" />
03 <setter Property="Margin" Value="0,2,40,2" />
04 <setter Property="Validation.ErrorTemplate">
05 </setter><setter .Value>
06 <controltemplate>
07 <dockpanel LastChildFill="true">
08 <border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
09 ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
10 <textblock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">
11 </textblock>
12 </border>
13 <adornedelementplaceholder Name="customAdorner" VerticalAlignment="Center" >
14 <border BorderBrush="red" BorderThickness="1" />
15 </adornedelementplaceholder>
16 </dockpanel>
17 </controltemplate>
18 </setter>
19 </style>

I’ve drawn the error icon very simply by wrapping a Border (with corner radius set properly) around a TextBlock. What’s of interest here is the AdornedElementPlaceholder, which like the name implies is a placeholder for an Adorner. So what is an Adorner? From my limited understanding, it’s basically a FrameworkElement that sits on top of your UI control on a different layer that is responsible for displaying visual cues. To me, it resembles a lot like the Decorator pattern, where you can enhance the look of your bound control without affecting it’s default state and behavior.

An example of customizing the Adorner is in the snippet above, where I put a red Border in it. When a validation error occurs, the Border appears around the control it is bound to, in this case the TextBox.

Lastly this line of Xaml code binds the ToolTip content to the first error message in the Validation.Errors collection.

1 ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"

WPF has loads of features, and I’m enjoying the process of discovery. It’s been great fun playing with it, and there will be more to come. Happy Coding.

Categories: Uncategorized