Implementing the IDataErrorInfo interface

The IDataErrorInfo interface is a very simple affair, with only two required properties to implement. The Error property returns the error message that describes the validation error, and the Item[string] indexer returns the error message for the specified property.

It certainly seems straightforward enough, so let's take a look at a basic implementation of this interface. Let's create another base class to implement this in and, for now, omit all other unrelated base class members so that we can concentrate on this interface:

using System.ComponentModel; 
using System.Runtime.CompilerServices; 
using CompanyName.ApplicationName.Extensions; namespace CompanyName.ApplicationName.DataModels { public abstract class BaseValidationModel : INotifyPropertyChanged, IDataErrorInfo { protected string error = string.Empty; #region IDataErrorInfo Members public string Error => error; public virtual string this[string propertyName] => error; #endregion #region INotifyPropertyChanged Members ... #endregion } }

 

In this simplest of implementations, we have declared a protected error field, which will be accessible to derived classes. Note that the Error property that returns it uses the C# 6.0 expression-bodied property syntax. This syntax is a shorthand notation for methods, properties, indexers, constructors, and destructors, where the member body is replaced by an inline expression.

We have declared the class indexer (the this property) as virtual, so that we can override it in the derived classes. Another option would be to declare it as abstract, so that derived classes were forced to override it. Whether you prefer to use virtual or abstract will depend on your particular circumstances, such as whether you expect every derived class to require validation.

Let's take a look at an example of a class that derives from our new base class:

using System; 
 
namespace CompanyName.ApplicationName.DataModels 
{ 
  public class Product : BaseValidationModel 
  { 
    private Guid id = Guid.Empty; 
    private string name = string.Empty; 
    private decimal price = 0; 
 
    public Guid Id 
    { 
      get { return id; } 
      set { if (id != value) { id = value; NotifyPropertyChanged(); } } 
    } 
 
    public string Name 
    { 
      get { return name; } 
      set { if (name != value) { name = value; NotifyPropertyChanged(); } }
    } 
 
    public decimal Price 
    { 
      get { return price; } 
      set { if (price != value) { price = value;
        NotifyPropertyChanged(); } } 
    } 
 
    public override string this[string propertyName] 
    { 
      get 
      { 
        error = string.Empty; 
        if (propertyName == nameof(Name)) 
        { 
          if (string.IsNullOrEmpty(Name))  
            error = "Please enter the product name."; 
          else if (Name.Length > 25) error = "The product name cannot be
            longer than twenty-five characters."; 
        } 
        else if (propertyName == nameof(Price) && Price == 0)
          error = "Please enter a valid price for the product."; 
        return error; 
      } 
    } 
  } 
} 

Here, we have a basic Product class that extends our new base class. The only job that each derived class that wants to participate in the validation process needs to do is to override the class indexer and supply details regarding their relevant validation logic.

In the indexer, we first set the error field to an empty string. Note that this is an essential part of this implementation, as without it, any triggered validation errors would never be cleared. There are a number of ways to implement this method, with several different abstractions being possible. However, all implementations require validation logic to be run when this property is called.

In our particular example, we simply use an if statement to check for errors in each property, although a switch statement works just as well here. The first condition checks the value of the propertyName input parameter, while multiple validation rules per property can be handled with inner if statements.

If the propertyName input parameter equals Name, then we first check to ensure that it has some value and provide an error message in case of failure. If the property value is not null or empty, then a second validation condition checks that the length is no longer than 25 characters, which simulates a particular database constraint that we may have.

If the propertyName input parameter equals Price, then we simply check that a valid, positive value has been entered and provide another error message in case of failure. If we had further properties in this class, then we would simply add further if conditions, checking their property names, and further relevant validation checks.

 

Now that we have our validatable class, let's add a new View and View Model and the DataTemplate in the App.xaml file that connects the two, to demonstrate what else we need to do to get our validation logic connected to the data in the UI. Let's first see the ProductViewModel class:

using CompanyName.ApplicationName.DataModels; 
 
namespace CompanyName.ApplicationName.ViewModels 
{ 
  public class ProductViewModel : BaseViewModel 
  { 
    private Product product = new Product(); 
 
    public Product Product 
    { 
      get { return product; } 
      set { if (product != value) { product = value;  
        NotifyPropertyChanged(); } } 
    } 
  } 
} 

The ProductViewModel class simply defines a single Product object and exposes it via the Product property. Let's now add some basic styles to the application resources file, which we'll use in the related View:

<Style x:Key="LabelStyle" TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,10,10" />
</Style>
<Style x:Key="FieldStyle" TargetType="{x:Type TextBox}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,0,10" />
<Setter Property="Padding" Value="1.5,2" />
</Style>

And now, let's see the View:

<UserControl x:Class="CompanyName.ApplicationName.Views.ProductView"  
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  Width="320" FontSize="14"> 
<Grid Margin="20"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Text="Name" Style="{StaticResource LabelStyle}" /> <TextBox Grid.Column="1" Text="{Binding Product.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Style="{StaticResource FieldStyle}" /> <TextBlock Grid.Row="1" Text="Price" Style="{StaticResource LabelStyle}" /> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Product.Price, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Style="{StaticResource FieldStyle}" /> </Grid> </UserControl>

In the XAML, we have a typical two column Grid panel, with two rows. The two TextBlock labels have the LabelStyle style applied, and the two TextBox input controls have the FieldStyle style applied. The binding applied to each TextBox.Text property has two important properties set on it.

The first is the UpdateSourceTrigger property, and this controls when the data source is updated and therefore, also when validation occurs. If you remember, a value of PropertyChanged causes updates to occur as soon as the data bound property value changes. An alternative value would be LostFocus, which causes updates to occur when the UI control loses focus, for example, when tabbing to the next control. 

The other important property here is the ValidatesOnDataErrors property, without which our current example would not work. Setting this property to True on a binding causes a built-in DataErrorValidationRule element to be implicitly added to the Binding.ValidationRules collection.

As the data bound value changes, this element will check for errors raised by the IDataErrorInfo interface. It does this by calling the indexer in our data Model, with the name of the data bound property each time the data source is updated. Therefore, in this basic example, developers would be responsible for setting this property to True on each binding to make the validation work.

In .NET 4.5, Microsoft introduced a breaking change to the way that numeric data is entered in the TextBox control when the UpdateSourceTrigger binding is set to PropertyChanged. Their change stops users from entering numerical separators. Refer to the Keeping Synchronized with Legacy Behavior section later in this chapter to find out why and how to work around this issue.

When using a value of PropertyChanged for the UpdateSourceTrigger property, along with the fact that we validate each time the properties change, we have the benefit of immediate updates of errors. However, this method of validation works in a pre-emptive manner, with all validation errors being shown before the user has a chance to enter any data. This can be somewhat off-putting to a user, so let's take a quick look at our example when it first starts:

As you can see, it's clear that there are some problems, but it's unclear as to what they are. So far, we have no output for our error messages. One common output that we could use would be the tooltips of the various form controls.

We could add a trigger to our FieldStyle style, which listened to the Validation.HasError Attached Property and set the TextBox control's tooltip to the ErrorContent property of the error whenever one was present. This is how Microsoft has traditionally demonstrated how to do this on their website:

<Style.Triggers> 
  <Trigger Property="Validation.HasError" Value="True"> 
    <Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0]. 
      ErrorContent, RelativeSource={RelativeSource Self}}" /> 
  </Trigger> 
</Style.Triggers> 

Note that we use brackets in the binding path for the Validation.Errors collection because it is an Attached Property, and that we use the RelativeSource.Self instance because we want to target the Errors collection of the TextBox control itself. Also note that this example only displays the first ValidationError object in the Errors collection.

 

Using this style on our data bound TextBox controls helps to provide the user with further information when they position their mouse cursor over the relevant control(s):

However, when there are no validation errors to display, an error will be seen in the Output window of Visual Studio, because we are attempting to view the first error from the Validation.Errors Attached Property collection, but none exist:

System.Windows.Data Error: 17 : Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:
Path=(Validation.Errors)[0].ErrorContent; DataItem='TextBox' (Name='');
target element is 'TextBox' (Name=''); target property is 'ToolTip' (type 'Object') ArgumentOutOfRangeException: 'System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: index'

There are a number of ways to avoid this error, such as simply displaying the whole collection, and we'll see an example of this later in the chapter. However, the simplest way is to make use of the CurrentItem property of the ICollectionView object that is implicitly used to wrap IEnumerable data collections, which are data bound to ItemsControl elements.

This is similar to the way that a ListBox will implicitly wrap our data bound data items in ListBoxItem elements. The implementation of the ICollectionView interface that wraps our data collection is primarily used to enable sorting, filtering, and grouping of the data, without affecting the actual data, but its CurrentItem property is a bonus in this situation.

With this, we can replace the indexer that was causing us a problem when there were no validation errors. Now, when there are no errors, the CurrentItem property will return null, rather than throwing an Exception and so, despite Microsoft's own example showing the use of the indexer, this is a far better solution:

<Setter Property="ToolTip" Value="{Binding (Validation.Errors). 
  CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" /> 

Nevertheless, if an end user is not aware of having to place their mouse cursor over the control to see the tooltip, then the situation is still not improved. Therefore, this initial implementation still has room for improvement. Another shortcoming of this interface is that it was designed to be atomic, so it only deals with a single error per property at a time.

In our Product class example, we want to validate the fact that the Name property is not only entered, but also has a valid length. In the order that we declared our two validation conditions for this property, the first error will be raised when the field in the UI is empty, and the second will be raised if the entered value is too long. As the entered value cannot be both non-existent and too long at the same time, having only a single reported error at one time is not a problem in this particular example.

However, if we had a property that had multiple validation conditions, such as a maximum length and a particular format, then with the usual IDataErrorInfo interface implementation, we'd only be able to view one of these errors at once. However, despite this limitation, we can still improve this basic implementation. Let's see how we can do this with a new base class:

using System.Collections.ObjectModel; 
using System.Collections.Specialized; 
using System.ComponentModel; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Text; 
using CompanyName.ApplicationName.Extensions; 
 
namespace CompanyName.ApplicationName.DataModels 
{ 
  public abstract class BaseValidationModelExtended : 
    INotifyPropertyChanged, IDataErrorInfo 
  { 
    protected ObservableCollection<string> errors =  
      new ObservableCollection<string>(); 
    protected ObservableCollection<string> externalErrors =  
      new ObservableCollection<string>(); 
 
    protected BaseValidationModelExtended() 
    { 
      ExternalErrors.CollectionChanged += ExternalErrors_CollectionChanged; 
    } 
 
    public virtual ObservableCollection<string> Errors => errors; 
 
    public ObservableCollection<string> ExternalErrors => externalErrors; 
 
    public virtual bool HasError => errors != null && errors.Any(); 
 
    #region IDataErrorInfo Members 
 
    public string Error 
    { 
      get 
      { 
        if (!HasError) return string.Empty; 
        StringBuilder errors = new StringBuilder(); 
        Errors.ForEach(e => errors.AppendUniqueOnNewLineIfNotEmpty(e)); 
        return errors.ToString(); 
      } 
    } 
 
    public virtual string this[string propertyName] => string.Empty; 
 
    #endregion 
 
    #region INotifyPropertyChanged Members 
 
    public virtual event PropertyChangedEventHandler PropertyChanged;

protected virtual void NotifyPropertyChanged( params string[] propertyNames) { if (PropertyChanged != null) { foreach (string propertyName in propertyNames) { if (propertyName != nameof(HasError)) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } PropertyChanged(this, new PropertyChangedEventArgs(nameof(HasError))); } } protected virtual void NotifyPropertyChanged( [CallerMemberName]string propertyName = "") { if (PropertyChanged != null) { if (propertyName != nameof(HasError)) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged(this, new PropertyChangedEventArgs(nameof(HasError))); } } #endregion private void ExternalErrors_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => NotifyPropertyChanged(nameof(Errors)); } }

In this example, we add two collections to hold error messages; the Errors collection property contains validation errors that are generated within the derived class, and the ExternalErrors collection property holds externally generated validation errors, typically from a parent View Model.

In the constructor, we attach the ExternalErrors_CollectionChanged event handler to the CollectionChanged event of the ExternalErrors collection property so that it is notified whenever items are added or removed from it.

After the declaration of the error collection properties, we see the HasError expression-bodied property, which checks whether the Errors collection contains any errors. Note that we check the errors field for null, rather than the Errors property, because calling the Errors property regenerates the error messages and we do not want to regenerate them all twice each time the HasError property is called.

Next, we see the new implementation of the IDataErrorInfo interface. The class indexer remains the same as the one from the previous implementation, but we see a difference in the definition of the Error property, which now compiles a complete list of all errors, rather than returning a single error message at a time.

In it, we first check whether any errors exist, and return an empty string if not. If errors do exist, we initialize a StringBuilder object and use our ForEach Extension Method to iterate through the Errors collection and append each of them to it, if they haven't already been included. We do this using another Extension Method before returning the output, so let's see what that looks like now:

public static void AppendUniqueOnNewLineIfNotEmpty(
  this StringBuilder stringBuilder, string text)
{ 
  if (text.Trim().Length > 0 && !stringBuilder.ToString().Contains(text))
    stringBuilder.AppendFormat("{0}{1}", stringBuilder.ToString().Trim().
    Length == 0 ? string.Empty : Environment.NewLine, text); 
}

 

In our AppendUniqueOnNewLineIfNotEmpty Extension Method, we first check that the input value is not an empty string and that it is not already present in the StringBuilder object. If the text input parameter is valid, we use the ternary operator to determine whether it is the first value to be added and whether we need to precede it with a new line or not, before adding the new, unique value.

Returning to our validation base class now, we see the new implementation of the INotifyPropertyChanged interface. Note that we repeat our earlier BaseSynchronizableDataModel class example by raising the PropertyChanged event each time changes are registered for any other properties, but, unlike the previous example, we raise the HasError property here, rather than the HasChanges property.

We can combine both of these and raise the PropertyChanged event for both properties each time we receive notification of changes to other properties if we so desire. In this case, the purpose is to call the HasError property, which will be used in the UI to display or hide the control that displays the error messages, and so it will be updated after every validatable property change.

At the bottom of our class, we see the expression-bodied ExternalErrors_CollectionChanged method, which calls the NotifyPropertyChanged method for the Errors collection property. This notifies controls that are data bound to this property that its value has changed and that they should retrieve that new value.

Let's see an example implementation of this now, using an extended version of our Product class:

public class ProductExtended : BaseValidationModelExtended
{
...

public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this[nameof(Name)]);
errors.AddUniqueIfNotEmpty(this[nameof(Price)]);
errors.AddRange(ExternalErrors);
return errors;
}
}

...
}

Therefore, when an error is externally added to the ExternalErrors collection, the ExternalErrors_CollectionChanged method will be called and this notifies changes to the Errors property. This results in the property being called and the external error(s) being added to the internal errors collection, along with any internal errors.

To get this particular implementation of the IDataErrorInfo interface to work, each data Model class will need to override this Errors property to add error messages from each validated property. We provide a few Extension Methods to make this task easier. As its name implies, the AddUniqueIfNotEmpty method adds strings to the collection if they do not already exist in it:

public static void AddUniqueIfNotEmpty(
  this ObservableCollection<string> collection, string text) 
{ 
  if (!string.IsNullOrEmpty(text) && !collection.Contains(text))  
    collection.Add(text); 
} 

The AddRange method is another useful Extension Method that simply iterates through the range collection input parameter and adds them to the collection parameter one by one:

public static void AddRange<T>(this ICollection<T> collection,  
  ICollection<T> range) 
{ 
  foreach (T item in range) collection.Add(item); 
} 

In addition to implementing this new Errors collection property in their derived classes, developers will also need to ensure that they notify changes to it each time a validatable property value is changed. We can do this using our overload of the NotifyPropertyChanged method that takes multiple values:

public string Name 
{ 
  get { return name; } 
  set { if (name != value) { name = value;  
    NotifyPropertyChanged(nameof(Name), nameof(Errors)); } } 
} 
 
public decimal Price 
{ 
  get { return price; } 
  set { if (price != value) { price = value;  
    NotifyPropertyChanged(nameof(Price), nameof(Errors)); } } 
}

The Errors property is responsible for calling the class indexer with the name of each of the properties that we want to validate. Any error messages that are returned, including those from the ExternalErrors collection property, are then added to the internal errors collection.

In effect, we have replicated what the Validation class and the DataErrorValidationRule element does in the UI, but in our data Model instead. This means that we no longer have to set the ValidatesOnDataErrors property to True on each binding. This is a better solution when using MVVM, as we prefer to work with data, rather than UI elements, and now also have full access to all of the data validation errors in our View Models.

Furthermore, we now have the ability to manually feed in error messages from our View Models to our data Models via the ExternalErrors collection property. This can be very useful when we need to validate across a collection of data Model objects.

For example, if we need to ensure that the name of each data Model object is unique within a collection of related objects, we can use this feature. Let's now create a new ProductViewModelExtended class to see how we can accomplish this:

using System;
using System.ComponentModel;
using System.Linq;
using CompanyName.ApplicationName.DataModels;
using CompanyName.ApplicationName.DataModels.Collections;

namespace CompanyName.ApplicationName.ViewModels
{
public class ProductViewModelExtended : BaseViewModel
{
private ProductsExtended products = new ProductsExtended();

public ProductViewModelExtended()
{
Products.Add(new ProductExtended() { Id = Guid.NewGuid(),
Name = "Virtual Reality Headset", Price = 14.99m });
Products.Add(new ProductExtended() { Id = Guid.NewGuid(),
Name = "Virtual Reality Headset" });
Products.CurrentItemChanged += Products_CurrentItemChanged;
Products.CurrentItem = Products.Last();
ValidateUniqueName(Products.CurrentItem);
}

public ProductsExtended Products
{
get { return products; }
set { if (products != value) { products = value;
NotifyPropertyChanged(); } }
}

private void Products_CurrentItemChanged(
ProductExtended oldProduct, ProductExtended newProduct)
{
if (newProduct != null)
newProduct.PropertyChanged += Product_PropertyChanged;
if (oldProduct != null)
oldProduct.PropertyChanged -= Product_PropertyChanged;
}

private void Product_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Products.CurrentItem.Name))
ValidateUniqueName(Products.CurrentItem);
}

private void ValidateUniqueName(ProductExtended product)
{
string errorMessage = "The product name must be unique.";
if (!IsProductNameUnique(product))
product.ExternalErrors.Add(errorMessage);
else product.ExternalErrors.Remove(errorMessage);
}

private bool IsProductNameUnique(ProductExtended product) =>
!Products.Any(p => p.Id != product.Id &&
!string.IsNullOrEmpty(p.Name) && p.Name == product.Name);
}
}

Like the ProductViewModel class, our ProductViewModelExtended class also extends the BaseViewModel class, but it declares a ProductsExtended collection and adds two ProductExtended objects to it in the constructor, instead of the single Product instance used previously. The ProductsExtended class simply extends our BaseCollection class:

namespace CompanyName.ApplicationName.DataModels.Collections 
{ 
  public class ProductsExtended : BaseCollection<ProductExtended> { } 
} 

In the class constructor, we first add a couple of test products to the ProductsExtended collection and then attach the Products_CurrentItemChanged method to its CurrentItemChanged delegate. In order to set the second item as the current item, we call the Last method on the ProductsExtended collection and set that to its CurrentItem property.

This ensures that the Products_CurrentItemChanged method is called when setting the second item as the current item and the Product_PropertyChanged handler is attached to it. After this, we then call the ValidateUniqueName method that is described shortly, passing in the current item.

After the declaration of the Products property, we see the Products_CurrentItemChanged method, which will be called each time the value of the CurrentItem property is changed. In it, we attach the Product_PropertyChanged method to the PropertyChanged event of the new, current ProductExtended object and detach it from the previous one. 

The Product_PropertyChanged method will be called each time any property of the related ProductExtended object changes. If the property that changed was the Name property, we call the ValidateUniqueName method, as that is the property that we need to validate for uniqueness.

The ValidateUniqueName method is responsible for adding or removing the error from the ExternalErrors collection property of the product input parameter. It does this by checking the result of the IsProductNameUnique method, which does the actual check for uniqueness.

In the expression-bodied IsProductNameUnique method, we use LINQ to query the Products collection and find out whether an existing item shares the same name. It does this by checking that each item does not have the same identification number, or, in other words, is not the object being edited, but does have the same name, and that the name is not an empty string.

If any other products that have the same name are found, then the method returns false and an error is added to the product's ExternalErrors collection in the ValidateUniqueName method. Note that we must manually remove this error if the name is found to be unique.

Let's now create a new ProductViewExtended class, to display these errors better. First, let's add another reusable resource to the application resources file:

<DataTemplate x:Key="WrapTemplate">
<TextBlock Text="{Binding}" TextWrapping="Wrap" />
</DataTemplate>

This DataTemplate simply displays a TextBlock control, with its Text property data bound to the data context of the DataTemplate, and its TextWrapping property set to Wrap, which has the effect of wrapping text that does not fit into the width provided. Now, let's look at the new ProductViewExtended class that uses this template:

<UserControl x:Class="CompanyName.ApplicationName.Views.ProductViewExtended"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" FontSize="14">
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Products}" SelectedItem="{Binding
Products.CurrentItem}" DisplayMemberPath="Name" Margin="0,0,20,0" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="2" BorderBrush="Red" BorderThickness="2"
Background="#1FFF0000" CornerRadius="5" Visibility="{Binding
Products.CurrentItem.HasError, Converter={StaticResource
BoolToVisibilityConverter}}" Margin="0,0,0,10" Padding="10">
<ItemsControl ItemsSource="{Binding Products.CurrentItem.Errors}"
ItemTemplate="{StaticResource WrapTemplate}" />
</Border>
<TextBlock Grid.Row="1" Text="Name"
Style="{StaticResource LabelStyle}" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding
Products.CurrentItem.Name, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource FieldStyle}" />
<TextBlock Grid.Row="2" Text="Price"
Style="{StaticResource LabelStyle}" />
<TextBox Grid.Row="2" Grid.Column="1"
Text="{Binding Products.CurrentItem.Price, Delay=250,
UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource FieldStyle}" />
</Grid>
</Grid>
</UserControl>

In this example, we now have a Grid panel with two columns. In the left column, we have a ListBox control, and, in the right column, we have another Grid panel containing our form fields. The ItemsSource property of the ListBox control is data bound to the Products collection property from our View Model, and the SelectedItem property is data bound to its CurrentItem property.

We set the DisplayMemberPath property to Name, to output the name of each product, as a shortcut for creating a DataTemplate for our Product class. Alternatively, we could have returned the value of the Name property from the ToString method in our Product class to achieve the same visual result, although that would not update in the UI when the property value changed.

In the Grid panel on the right, we declare three rows and, in the top one, we define a Border element containing an ItemsControl object. Its ItemsSource property is data bound to the Errors collection property of the item that is set to the CurrentItem property of the Products collection, and its ItemTemplate property is set to our new WrapTemplate data template. The Visibility property of the border is data bound to the item's HasError property using the BoolToVisibilityConverter instance from the application resources.

Therefore, when a change is made to a validated property of the item and an error is raised in our validation base class, the PropertyChanged event is raised for the HasError property and this alerts this binding to check the latest value and update its visibility value via the applied BoolToVisibilityConverter instance accordingly.

Note that we use ItemsControl here, because with this collection, we have no need for the extra features that the ListBox control provides us with, such as a border, or the notion of a selected item. The two rows underneath the error output contain the form fields from the ProductView example.

When this example is run, we'll see two items that have the same name in our ListBox control. As such, there will already be a validation error displayed that highlights this fact and that was added through the ExternalErrors collection in the View Model.

In addition to this, we'll see another error, highlighting the fact that a valid price needs to be entered:

As the UpdateSourceTrigger property of the field bindings have been set to PropertyChanged and the data bound properties are validated straight away, the errors will immediately disappear and/or reappear as soon as we type in the relevant form fields. This setting, along with the fact that we validate each time the properties change, makes our validation work in a pre-emptive manner.

We can also change this to work only when a user presses a submit button by setting the UpdateSourceTrigger property to the Explicit value. However, this requires that we access the data bound controls in the code behind files and so we tend to avoid this approach when using the MVVM methodology:

BindingExpression bindingExpression =  
  NameOfTextBox.GetBindingExpression(TextBox.TextProperty); 
bindingExpression.UpdateSource(); 

Alternatively, if we wanted to validate in this way when using MVVM, we could simply call the validation code when the command that is data bound to the submit or save button is executed instead. Let's now take a look at the INotifyDataErrorInfo interface to see how it differs from the IDataErrorInfo interface.