Friday, November 18, 2011

Exceptions and Validation

Validation is only a concern for TwoWay data bindings. When Silverlight moves data from your view back into your model – how do you control the values your model accepts? And how do you inform the user when they’ve entered illegal input?

First, let’s talk about the problems that can occur when data flows back into your model. The first potential problem is that Silverlight may not be able to convert the user’s input into the data type the model requires. This can happen when the user types “abc” into a field that expects integers, or types “123” into a field that requires a date. Some of these problems you try to avoid by using restricted controls (a calendar control for the user to enter a date, as an example), but this won’t avoid all conversion problems.

Another type of problem happens when Silverlight can convert the user’s input into the proper type, but your business logic determines the value of the data is illegal. One example is trying to place an order for –1 books. You want the quantity value of an order to always be positive. In these cases you must your model throw an exception when Silverlight sets the value. How do you know the exception occurred?

Silverlight can tell you about both types of validation errors, but you must set two properties on a binding to true. The first property is the ValidatesOnExceptions property. This property tells Silverlight to catch exceptions that occur when data moves from the view to the model. Again – this will be the exceptions thrown when trying to convert the user’s input, AND any exceptions thrown by the model itself.

In order to know about these exceptions you must also set the NotifyOnValidationError property to true. When true, Silverlight will raise a BindingValidationError event. This event will bubble up the visual tree of elements, so you can subscribe to the event on the element that is data bound, or at the element’s parent level, or the parent’s parent, and so on. In the following XAML we subscribe at the UserControl level.

<UserControl Width="400"      x:Class="DataBindingDemo.EditTimeCard"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:local="clr-namespace:DataBindingDemo"      BindingValidationError="UserControl_BindingValidationError" >     <UserControl.Resources>         <local:DateFormatter x:Key="dateFormatter" />     UserControl.Resources>     <Grid x:Name="LayoutRoot" Background="White">         <StackPanel>             <TextBlock Text="Total Hours"/>             <TextBox Margin="10" Text="{Binding                                          ValidatesOnExceptions=true,                                         NotifyOnValidationError=true,                                         Mode=TwoWay,                                         Path=TotalHours}"/>             <TextBlock Text="Start Date"/>              <TextBox Margin="10" Text="{Binding                                         Mode=TwoWay,                                         Converter={StaticResource dateFormatter},                                         ConverterParameter='{0:d}',                                         Path=PayPeriod.Start}"/>             <TextBlock Text="End Date"/>             <TextBox Margin="10" Text="{Binding Path=PayPeriod.End}"/>                     StackPanel>     Grid> UserControl> 

The BindingValidationError event can tell you the source of an error, and also if the error is being added (the user just entered bad input) or removed (the user corrected a bad input). We can use this information to change elements on the screen, such as making the control that is the source of the failure turn red.

private void UserControl_BindingValidationError(     object sender, ValidationErrorEventArgs e) {     var control = e.OriginalSource as Control;     switch (e.Action)     {         case ValidationErrorEventAction.Added:             control.Background = new SolidColorBrush(_red);             break;         case ValidationErrorEventAction.Removed:             control.Background = new SolidColorBrush(_white);             break;     } } 

No comments:

Post a Comment