It’s impossible to have a typical forms over data business application without handling validation in the UI.
Silverlight 3 has excellent simple to use support for implementing validation in your code. This post will show a simple scenario to implement data validation. The code for this post can be found here.
Steps:
1. Create a Silverlight application.
2. Add a class called Person and add the following code to the Person class:
private string firstName;
private string lastName;
private string email;
public string FirstName
{
get { return firstName; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("First name in required.");
firstName = value;
}
}
public string LastName
{
get { return lastName; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Last name in required.");
lastName = value;
}
}
public string Email
{
get { return email; }
set
{
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?");
if (!r.IsMatch(value))
throw new ArgumentException("A valid email is required.");
email = value;
}
}
Note that this class has 3 properties – FirstName, LastName and Email. Validation is carried out in the setter of each property to determine if a valid value is supplied. If validation fails for a property an exception (in this case ArgumentException) is thrown. This is the mechanism to tell Silverlight that something bad happened in setting the value of an object.
3. Add the following XAML to the LayoutRoot Grid in the MainPage.xaml file:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="First Name:" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" ></TextBlock>
<TextBlock Text="Last Name:" Grid.Column="1" Grid.Row="2" VerticalAlignment="Center" ></TextBlock>
<TextBlock Text="Email:" Grid.Column="1" Grid.Row="3" VerticalAlignment="Center" ></TextBlock>
<TextBox x:Name="FirstNameTextBox" Grid.Column="2" Grid.Row="1" Text="{Binding Person.FirstName, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}"></TextBox>
<TextBox x:Name="LastNameTextBox" Grid.Column="2" Grid.Row="2" Text="{Binding Person.LastName, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}"></TextBox>
<TextBox x:Name="EmailTextBox" Grid.Column="2" Grid.Row="3" Text="{Binding Person.Email, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}"></TextBox>
<Button x:Name="SaveButton" Content="Save" Click="SaveButton_Click" Grid.Column="2" Grid.Row="4" ></Button>
<ListBox x:Name="ErrorsListBox" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="5" BorderThickness="0" DisplayMemberPath="ErrorContent" Height="auto" Foreground="Red" FontWeight="Bold" >
</ListBox>
This XAML defines the UI for the application and binds 3 text boxes to FirstName, LastName and Email properties of an object of our Person type.
The ErrorsListBox control at the bottom will be used to display any validation errors.
The key points to note are on the text box bindings:
a. First their mode = TwoWay – so when they are updated in the UI their data source is updated; i.e. the setters of the properties on the Person object are called with updated values.
b. ValidatesOnExceptions = True – this tells Silverlight that if an exception occurs when the Silverlight Property System is updating the setters of a bound property, the validation template for the control is displayed to the user with the details of the exception (i.e. the exception’s message string) that was thrown in the bound setter.
c. NotifyOnValidationError – True – this tells Silverlight to raise a routed event of type BindingValidationError to be routed up the visual tree that can be handled by a parent control – in our case the LayoutRoot Grid.
4. Add the following attribute to the LayoutRoot Grid:
BindingValidationError="LayoutRoot_BindingValidationError"
This adds the event handler for when the text boxes cause Silverlight to raise the routed event of type BindingValidationError and allows us to add logic to the event handler to handle the routed error.
5. Replace the MainPage constructor in the MainPage class in the MainPage.xaml.cs class with the following code:
private ObservableCollection<ValidationError> errors = new ObservableCollection<ValidationError>();
public ObservableCollection<ValidationError> Errors
{
get { return errors; }
set { errors = value; }
}
private Person person = new Person();
public Person Person
{
get { return person; }
set { person = value; }
}
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this;
}
This defines an ObservableCollection to hold the ValidationErrors that occur when binding validation fails (we will get to this in a moment). The collection is made available through a property so it can be bound to by the ListBox.
A Person object is instantiated and made available through a property in this class so that the three text boxes can be bound to its properties.
When the page is loaded the DataContext of the UserControl is bound to this class.
6. Paste the following code into the MainPage class:
private void LayoutRoot_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
switch(e.Action)
{
case ValidationErrorEventAction.Added:
errors.Add(e.Error);
break;
case ValidationErrorEventAction.Removed:
errors.Remove(e.Error);
break;
}
ErrorsListBox.ItemsSource = this.errors;
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
foreach (DependencyObject control in LayoutRoot.Children)
{
TextBox textbox = control as TextBox;
if (textbox != null)
{
BindingExpression expr = textbox.GetBindingExpression(TextBox.TextProperty);
expr.UpdateSource();
}
}
}
This code defines two event handlers:
a. The first is the event handler called when a child control of the LayoutRoot Grid control throws a binding exception. For each exception thrown this event handler will be called by Silverlight. Notice we identify if the ValidationError object passed into this event handler has been added or removed – i.e. a validation failed or passed (i.e. the error was removed by Silverlight). If the validation error is added, it is added to our errors collection; if it was removed it is removed from our errors collection. The errors collection is then bound to the list box so the error messages are displayed to the users.
b. The second loops through the text boxes in the UI and forces their binding expressions to update their bound data sources – forcing the setters on the Person object to be called.
7. Run the application:

8. Click the save button:

See that all fields in the Person object threw exceptions that are displayed in the list box and the text boxes are highlighted in red.
9. Set focus to the first name text box.

Note the popup label that displays the error message associated with the Person object property FirstName.
10. Type a character in the first name textbox and tab out:

Notice the validation error label is no longer displayed for the first name property and the error message has disappeared from the list box. Also note the textbox for the first name is no longer red.
11. Type a character in the last name textbox and tab out and enter a valid email address in the email text box and click the Save button:

Congratulations! You have implemented validation in a custom object and hooked it up to use the standard Silverlight validation handlers.
What have we done?
We have created our own custom object with validation on its properties.
We have bound the custom object to Silverlight controls.
With configuration we have hooked up the standard Silverlight validation mechanisms for the bound controls.
We have hooked up the normal mechanism for a container control to trap and handle validation errors from child controls and display them to the user.
Conclusion
Silverlight has build in UI validation handling features that easily allow a developer to hook up validation in custom classes to controls in the UI layer and to capture and manage validation errors at a higher level in the control hierarchy in whatever way makes sense for their application. Once again we see how Silverlight supports developing custom business applications with the infrastructure it delivers – with minimal coding by the developer to harness the value add supplied by Silverlight.