Saturday, July 17, 2010

WPF Validation - IDataErrorInfo

This is the third post in the series of discussion about WPF validation. In these posts, we have been discussing the various validation techniques available in Windows Presentation Foundation.

In this post, we will be discussing about IDataErrorInfo.

What is IDataErrorInfo?
IDataErrorInfo is an interface defined in System.ComponentModel namespace. The contract required implementing indexer and a string property named Error.

Let's see IDataErrorInfo in Practice!
We create a window, named Window3. To keep the example simple, we just have a text box in this window, textBox1. The important thing is the binding of this text box. While setting the binding to Name property of the data context, we have also set ValidateOnDataErrors as true. This would use IDataErrorInfo members from the data context for validation of its properties.

<Window x:Class="wpf_validation.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3" Height="300" Width="557">
<Grid>
<TextBox Height="23" Margin="123,32,107,0" Name="textBox1" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" />
</TextBox.Text>
</TextBox>
</Grid>
</Window>

Now let's have a look at the code behind of Window3.
public partial class Window3 : Window
{
Window3ViewModel _vm;

public Window3()
{
InitializeComponent();
_vm = new Window3ViewModel();
this.DataContext = _vm;
}
}

As you can see above, we have created an object of Window3ViewModel and assigned it as the data context of the window. Below you can see the definition of Window3ViewModel. As discussed above, since we are using ValidatesOnDataErrors, we need to implement our data context with IDataErrorInfo interface. Additionally, our view is expecting Name property from our data context. Since we will be defining it as dependency property, we need to inherit from DependencyObject which provides GetValue and SetValue method used in the dependency property.
class Window3ViewModel : DependencyObject, IDataErrorInfo
{
public string Name
{
get
{
return (string)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Window3ViewModel));

#region IDataErrorInfo Members

string IDataErrorInfo.Error
{
get
{
return null;
}
}

string IDataErrorInfo.this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case "Name":
if (!string.IsNullOrEmpty(Name) && System.Text.RegularExpressions.Regex.IsMatch(Name, "[/!@#?/}[}{]"))
{
result = "Invalid Characters in name!";
}

break;
default:
break;
}
return result;
}
}

#endregion
}

In the above code, to implement IDataErrorInfo, we have provided definition of the indexer. This would be called for each binding for which we have set ValidatesOnDataErrors as true. This would be called with name of property as the argument. In this example, it is columnName. This helps us in finding out which property is being set. Then we can provide the validation logic for the particular property. The interesting thing is that we don't have anything like value which gives us some idea about the property being set. As a matter of fact we can directly use property (in our case Name) to get the value being set. This is because this member gets called after property being set with value. So, even if, the validation results in error, we still have the property with invalid value. We might need to check it again before submission of the form if we don't want incorrect values to be submitted.

What is IDataErrorInfo.Error used for?
As you might have noticed, IDataErrorInfo.Error always returns null. Actually we did not need this property for our example. This is used in cases when you need to provide notification message to the user about validation errors. This provides you a single property which you can bind to your notification control in UI.

Note:
The issue with IDataErrorInfo is that no matter though we get the status of validation but the value is still set in the property being bound. Actually the validation logic is executed after setting the value. Though it is still useful for display of error information to the user. Before submitting the form, we still need to validate if all data is in valid state.

This is one of the validation techniques when UI has no idea about how the data should be validated.

No comments: