Saturday, July 17, 2010

WPF Validation - ValidateValueCallback

This is a series of discussions about different validation options in WPF. In this discussion, we will be discussing about validation through dependency property's ValidateValueCallback method.

What is ValidateValueCallBack
It is the default validation applied to a dependency property. Whenever the property value is set, the WPF runtime checks its value using this. If this is a valid value then this method should return true otherwise it should return false.

How to specify ValidateValueCallBack?
This is specified at the same time when we are registering the property with WPF runtime. It is one of the parameter of Register static method in DependencyProperty class.

What actually happens at the background?
When the dependency property is set with a value which has decided to be an invalid value in the ValidateValueCallback then it returns false. Whenever WPF runtime finds it false it results in an exception. If ValidatesOnException is set as true when the control's property is bound then the control is set with Error template.

Simple Example:
Let us create a small WPF window as follows:
<Window x:Class="wpf_validation.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2" Height="300" Width="300">
<Grid>
<TextBox Height="23" Margin="12,19,12,0" Name="textBox1" VerticalAlignment="Top" >
<Binding Path="Name" UpdateSourceTrigger= "PropertyChanged"
ValidatesOnExceptions="True">
</Binding>
</TextBox>
</Grid>
</Window>

As you can see that we have bound Text property of textBox1 text box with Name property of data context. The other thing worth noticing is setting ValidatesOnExceptions as true.

Now have a look at the code behind of this window. In the constructor, we are setting Window2ViewModel as the data context.
public partial class Window2 : Window
{
Window2ViewModel _vm;
public Window2()
{
InitializeComponent();
_vm = new Window2ViewModel();
this.DataContext = _vm;
}
}

The definition of Window2ViewModel is as follows:
class Window2ViewModel : DependencyObject
{
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Window1ViewModel), null, new ValidateValueCallback(isNameValid));

private static bool isNameValid(object value)
{
bool ret = true;
string val = (string)value;

if (!string.IsNullOrEmpty(val) &&
System.Text.RegularExpressions.Regex.IsMatch(val, "[/!@#?/}[}{]"))
ret = false;
else
ret = true;

return ret;
}

}

So we have a dependency property, named Name in the view model. While registering this property we have specified the ValidateValueCallback method as isNameValid method. You can notice that as the dependency property isNameValid is also static in nature. We are checking the value being set for having some special character. If it contains some invalid character then it returns false. As we discussed above if binding is set with ValidatesOnException as true then the user is shown with error template on the control. In our case, if we enter any special character in the text box, it is displayed as follows:



Note:
This technique for validation is specially useful when the user interface has no idea about any validation logic. The validation is the responsibility of whatever is set as data context (in this case view model). As you might have notice when we change the validation logic, we just change it in the Window2ViewModel. This is specially useful when the data context is available in the form of libraries. The data context library providers can make sure that no invalid value is set in any property.

2 comments:

Anonymous said...

Excellent article on dependency properties,so powerful

Anonymous said...

great job,
almost nobody on web had a answer on how to handle the exception when ValidateValueCallback returns false. You explained it. Thanks.

Love from India.