Page view counter

Jesse Liberty - Silverlight Geek

More Signal Less Noise

Data Binding – Data Validation

SLLogoWords

The third and final Data Binding topic I'd like to cover for now is Data Validation. I'll modify the code discussed yesterday we're going to validate the Quantity on Hand field, already marked as two-way (that is, the user can enter a new value and it is stored in the underlying business object – the current book).

 DataBindingArchitectureTwoWay

Our goal is to capture and handle two types of errors

  • Exceptions thrown from the Binding Engine
  • Exceptions thrown from the Binding Source

An example of an exception the Binding Engine would throw would be were the user to enter letters where an integer value is expected

Binding Source exceptions would be created by the author of the Binding Source class (the Book Class) to ensure data integrity. In our case, we might throw an exception if we are handed a negative value for Quantity on hand. Thus, we might modify the set accessor for Quantity on hand as follows,

 

   1: public class Book : INotifyPropertyChanged
   2: {
   3:    private int quantityOnHand;
   4:      // Snip 
   5:  
   6:    public int QuantityOnHand
   7:    {
   8:       get { return quantityOnHand; }
   9:       set
  10:       {
  11:          if ( value < 0 )
  12:          {
  13:             throw new Exception( "Quantity on hand cannot be negative!" );
  14:          }
  15:          quantityOnHand = value;
  16:          NotifyPropertyChanged( "QuantityOnHand" );
  17:       }       // end set
  18:    }
  19:  

To manage both of these types of errors need to take 3 steps

  1. Identify the error handler either in the control or higher in the visiblity hierarchy (e.g., a container; in this case the grid that contains the text box)
  2. Set NotifyOnValidationError and ValidateOnException to true. The latter tells teh Binding Engine to create a validation error event when an exception occurs. The former tells the Binding Engine to raise teh BindingValidationError event when a validation error occurs.
  3. Create the event handler named in step 1

Identify the Handler

We'll put the identifier of the handler in the grid so that it may be shared by all the controls within the grid.

<Grid x:Name="LayoutRoot" Background="White" BindingValidationError="LayoutRoot_BindingValidationError" >

Set NotifyOnValidationError and ValidateOnException

These are set as part of the binding syntax in the control itself,

   1: <TextBox x:Name="QuantityOnHand"   
   2:     Text="{Binding Mode=TwoWay, Path=QuantityOnHand, 
   3:         NotifyOnValidationError=true,  ValidatesOnExceptions=true }"
   4:     VerticalAlignment="Bottom"
   5:     HorizontalAlignment="Left"
   6:     Height="30" Width="90"
   7:     Grid.Row="4" Grid.Column="1" />

Create the Event Handler

The event handler named in the grid is implemented in the associated code-behind file,

   1: public partial class Page : UserControl
   2: {
   3:    private bool clean = true;  // new flag
   4:  
   5:    void Change_Click( object sender, RoutedEventArgs e )
   6:    {  // if not clean, don't change books
   7:       if ( !clean )
   8:          return;
   9:       // snip
  10:    }
  11:  
  12:    private void LayoutRoot_BindingValidationError( 
  13:       object sender, ValidationErrorEventArgs e )
  14:    {
  15:       if ( e.Action == ValidationErrorEventAction.Added )
  16:       {
  17:          QuantityOnHand.Background = new SolidColorBrush( Colors.Red );
  18:          clean = false;
  19:       }
  20:       else if ( e.Action == ValidationErrorEventAction.Removed )
  21:       {
  22:          QuantityOnHand.Background = new SolidColorBrush( Colors.White );
  23:          clean = true;
  24:       }
  25:    }

Delegation of Responsibility

Notice the careful delegation of responsibility. 

The Binding class knows that it is handling validation and some first approximation of validity (integers aren't text) but it turns to the Book class for further validation (e.g, Quantity on Hand can't be negative). If it finds that the value is invalid it raises an event, but it is up to the UI to handle that event and decide how to display that there is invalid data to the user.

Page.xaml in this case manages that by refusing to move to the next book and turning the input box red. There are a thousand other things it could do, but the key here is separation of responsibility. The UI doesn't know why the data is invalid, only that it is.

Here is what it looks like when you enter invalid data and hit the Change Book button

InvalidData

As you can see, the entry box turns red, and the book does not change until the user fixes the data entered.

For those of you who want to copy and paste, here are all the changes

// page.xaml.cs

private bool clean = true;


private void LayoutRoot_BindingValidationError( 
   object sender, ValidationErrorEventArgs e )
{
   if ( e.Action == ValidationErrorEventAction.Added )
   {
      QuantityOnHand.Background = new SolidColorBrush( Colors.Red );
      clean = false;
   }
   else if ( e.Action == ValidationErrorEventAction.Removed )
   {
      QuantityOnHand.Background = new SolidColorBrush( Colors.White );
      clean = true;
   }
}



// page.xaml

<Grid x:Name="LayoutRoot" Background="White" BindingValidationError="LayoutRoot_BindingValidationError" >

<TextBox x:Name="QuantityOnHand"   
    Text="{Binding Mode=TwoWay, Path=QuantityOnHand, 
        NotifyOnValidationError=true,  ValidatesOnExceptions=true }"
    VerticalAlignment="Bottom"
    HorizontalAlignment="Left"
    Height="30" Width="90"red
    Grid.Row="4" Grid.Column="1" />


// book.cs

public int QuantityOnHand
{
   get { return quantityOnHand; }
   set
   {
      if ( value < 0 )
      {
         throw new Exception( "Quantity on hand cannot be negative!" );
      }
      quantityOnHand = value;
      NotifyPropertyChanged( "QuantityOnHand" );
   }       // end set
}

Comments

party42 said:

Jesse,

interesting read. Two questions though, is the base Exception object the appropriate object to be throwing? Any inherited object might be better?

Besides that, doing validation inside your object model. Doesn't that kind of bind your objects (your model) to a particular GUI. In other words, would that be an advised practice?

Regards, Nathan

# October 13, 2008 3:03 PM

Data Binding ??? Data Validation said:

Pingback from  Data Binding ??? Data Validation

# October 13, 2008 7:28 PM

jiafeng said:

Jesse,

Great article! Could you give an example how to do this with LinqToSQL class? They are automatically generated and implemented INotifyPropertyChanged but it only exposed property changed event, How do I modify its set assessor then?

Thanks, Jia

# October 13, 2008 8:49 PM

2008 October 14 - Links for today « My (almost) Daily Links said:

Pingback from  2008 October 14 - Links for today &laquo; My (almost) Daily Links

# October 14, 2008 4:02 AM

Silverlight news for October 14, 2008 said:

Pingback from  Silverlight news for October 14, 2008

# October 14, 2008 5:14 AM

party42 said:

Jesse, are you also planning to talk about data integrity and concurrency issues?

We've run into some major issues regarding the sorta disconnected envirment silverlight is and its data being on a remote location.

To clarify, we have a SL app which stores its data both locally (for speed) and remotely (for obvious reasons). To access this data, the SL environment uses a webservice. This webservice is also publically available for hooking up to other clients. To keep the data in sync we use a "datelastmodified" (isdirty switch) on both data sources. Though checking this on every move a user makes takes time (about 1 second every time).

Ideally I guess having a full duplex service would be a solution but Im not sure if thats even possible with services. How would a service know its client...

That sounds scary anyway :)

# October 14, 2008 8:02 AM

Data Binding in Silverlight said:

Pingback from  Data Binding in Silverlight

# November 20, 2008 10:49 PM

Data Binding in Silverlight with Jesse Liberty said:

Pingback from  Data Binding in Silverlight with Jesse Liberty

# November 24, 2008 3:35 PM

??berblick Data-Binding at Programming with Silverlight, WPF & .NET said:

Pingback from  ??berblick Data-Binding at Programming with Silverlight, WPF &amp; .NET

# November 24, 2008 5:16 PM

Jesse Liberty - Silverlight Geek said:

Happy to return to my monthly interview on the Sparkling Client podcast; this month discussing the fundamentals

# November 25, 2008 11:40 AM

Microsoft Weblogs said:

Happy to return to my monthly interview on the Sparkling Client podcast; this month discussing the fundamentals

# November 25, 2008 12:28 PM