Data validation – Silverlight versus WPF

When I am creating business applications validating the data in business objects, and displaying any errors, is always a big deal. Even though WPF and Silverlight both use XAML and support data binding the default way to display errors is quite different. Now I am focusing on validation in the business object here even though WPF has additional capabilities of validating input in the XAML using ValidationRules. While there might be some good reason to validate data directly in the UI in some cases I find it a requirement to validate data inside a business object so the validation is done regardless of the way the business object is configured.

Both with Silverlight and WPF it is possible to create business objects that validate their properties. Unfortunately the normal way to do this is by throwing an exception in the property setter if the validation fails. Now I don’t really like this approach, in fact I prefer the approach taken by Rocky Lhotka in CSLA or Paul Stovell in his article about Delegates and Business Objects. But that is a subject for another post, lets first take a look at the out of the box behavior.

The test "business object" I am working with is real simple. In fact so simple that I don’t even implement INotifyPropertyChanged, something I recommend you always do. But for the purposes of validation it serves no useful purpose so I left it out. The Person class, used both in WPF and Silverlight looks like this:

public class Person
{
    public string FirstName { get; set; }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            if (string.IsNullOrEmpty(_lastName))
                throw new ArgumentNullException();
        }
    }
}


Pretty simple right, the "business rule" here is that the LastName property cannot be empty.



 



Validation in WPF



The XAML used in the WPF sample looks like this:



<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="200" Width="400" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Firstname:" Grid.Row="0" Grid.Column="0"/>
        <TextBox Text="{Binding FirstName}" Grid.Row="0" Grid.Column="1"/>
        <TextBlock Text="Lastname:" Grid.Row="1" Grid.Column="0"/>
        <TextBox Grid.Row="1" Grid.Column="1">
            <TextBox.Text>
                <Binding Path="LastName">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule/>
                    </Binding.ValidationRules>
                </Binding>                
            </TextBox.Text>
        </TextBox>
        <Button Content="Close" Grid.Row="2" Grid.Column="0"/>
    </Grid>
</Window>


Not very complex but as we can see there is quite a difference between the FirstName TextBox with no validation and the LastName TextBox which must handle the not empty validation. In order for the UI to know about the validation we need to add the Binding.ValidationRules element and add to that the ExceptionValidationRule. The ExceptionValidationRule basically says that any exception raised during the property setter is a failed validation rule. Not very complicated but it does add quite a bit of XAML just to be able to display the fact that something is wrong. When we run the code and empty the LastName TextBox the result looks like this:



image



The red line around the TextBox indicates that there is some kind of error. Where does the red box come from? Well that is just the default ErrorTemplate used and you are free to change it by setting the Validation.ErrorTemplate property on the TextBox.



 



Validation in Silverlight



The XAML in Silverlight looks somewhat different. The main XAML is pretty much the same but as soon as we get into data binding things look very different. See for yourself:



<UserControl x:Class="SilverlightApplication1.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Firstname:" Grid.Row="0" Grid.Column="0"/>
        <TextBox Text="{Binding FirstName}" Grid.Row="0" Grid.Column="1"/>
        <TextBlock Text="Lastname:" Grid.Row="1" Grid.Column="0"/>
        <TextBox Grid.Row="1" Grid.Column="1">
            <TextBox.Text>
                <Binding Path="LastName" Mode="TwoWay" 
                         NotifyOnValidationError="True" 
                         ValidatesOnExceptions="True" />
            </TextBox.Text>
        </TextBox>
        <Button Content="Close" Grid.Row="2" Grid.Column="0"/>
    </Grid>
</UserControl>



 



This time we need to set the NotifyOnValidationError and ValidatesOnExceptions properties of the binding to true. In WPF these properties don’t exist and we used the ValidationRules and ExceptionValidationRule to reach the same effect. So what happens when we run this and clear out the LastName TextBox? Actually the validation fails, the exception is thrown and we don’t see anything at all [:(]. The reason is that in Silverlight there is no Validation.ErrorTemplate, instead we have to do something to let the user know an error occurred. In order to do so we need to subscribe to the BindingValidationError event that fires as soon as a validation fails. To achieve a similar effect as the WPF application we could use some code like this:



public Page()
{
    InitializeComponent();
    BindingValidationError += new EventHandler<ValidationErrorEventArgs>(Page_BindingValidationError);
}

private Brush _borderBrush = null;
void Page_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    Control control = (Control)e.OriginalSource;

    if (_borderBrush == null)
        _borderBrush = control.BorderBrush;

    switch (e.Action)
    {
        case ValidationErrorEventAction.Added:
            control.BorderBrush = new SolidColorBrush(Colors.Red);
            break;
        case ValidationErrorEventAction.Removed:
            control.BorderBrush = _borderBrush;
            break;
        default:
            break;
    }
}



Not very complicated but still we need to do so in code. Of course the BindingValidationError event doesn’t exist in WPF either so non of this XAML or code would post from Silverlight back to WPF [:(].



The effect is the same as in WPF however and looks like this:



image



 



Conclusion



Validation is quite different in WPF and Silverlight making for non portable XAML and code. However the biggest deal is that validation, at least on the WPF side, is more focused on the UI that the business layer where it should be. And the small part where the validation in the UI is used it is completely focused on exceptions and not in business rules, something far superior. So for any serious business application this subject needs some significant work, just like Rock Lhotka had to do with his CSLA framework.



[f1]
[f2]

2 thoughts on “Data validation – Silverlight versus WPF

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>