Thursday, 10 March 2011

Paired programming: How to use Data Annotations in WPF without throwing Validation exceptions


We want to add simple data validation rules directly on the property within the view model. Data annotation provides attributes designed to do exactly this. There are some great websites that describe how to do this

From http://msdn.microsoft.com/en-us/library/dd901590(v=vs.95).aspx we have a great example of how this is done in Silverlight using a sdk which includes a modified datagrid and a DescriptionViewer ( which I will return to later). Within this website we can find a wide range of simple validation attributes as illustrated in the sample shown below:



public class Product
{
[Display(Name="Product Number")]
[Range(0, 5000)]
public int ProductID { get; set; }

[Display(Name="Name")]
[Required]
public string ProductName { get; set; }

[Display(Name="Price")]
[DataType(DataType.Currency)]
public double ListPrice { get; set; }

[EnumDataType(typeof(ProductColor))]
public ProductColor Color { get; set; }

[Display(Name="Available")]
public bool InStock { get; set; }

public Product() { }

public Product(int _productID, string _productName,
double _listPrice, ProductColor _color, bool _inStock)
{
this.ProductID = _productID;
this.ProductName = _productName;
this.ListPrice = _listPrice;
this.Color = _color;
this.InStock = _inStock;
}
}



It is also possible to validate regular expressions eg

[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage ="….")]

We want to incorporate this into our view model because these attributes help to document the property and provide a mechanism to validate against. Here's another great web site that goes through the steps of adding this to a WPF application:

http://outcoldman.ru/en/blog/show/259

If we run through the steps described in these websites and combine with our modified INotifyPropertyChanged base class that I described in my last blog entry we end up with a model-view that looks like:



using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace Ccp.Wpf.Infrastructure.Entities
{
public class FranchiseStepPayouts : ModelSettingsBase
{
private double franchiseDeductiblePercentage;
[Display(Name = "Franchise deductible percentage", Description = "Blanket Franchise deductible percentage used to bla bla")]
[Range(0, 100, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public double FranchiseDeductiblePercentage
{
get
{
return franchiseDeductiblePercentage;
}
set
{
Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName = "FranchiseDeductiblePercentage" });
franchiseDeductiblePercentage = value;
RaisePropertyChanged(() => FranchiseDeductiblePercentage);
}
}




The validation happens on line

Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "FranchiseDeductiblePercentage" });

The corresponding view looks like:



<UserControl.Resources>
<local:EnumMatchToBooleanConverter x:Key="enumConverter" />
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">*</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>


...



<!-- Franchise -->
<StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="0">
<TextBox Text="{Binding FranchiseDeductiblePercentage, Mode= TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}"
Width="100"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}" />
<Label Content="%" Height="23" VerticalAlignment="Top" />
</StackPanel>


Breaking this down into parts: The text box contains a binding with an a term "ValidatesOnExceptions=true" This tells the view that it should listen for Validation exceptions that are thrown in the view model. This happens in the setter with the Validator.ValidateProperty. So in our case we have decorated our FranchiseDeductiblePercentage with

[Display(Name = "Franchise deductible percentage", Description = "Blanket Franchise deductible percentage used to bla bla")]
[Range(0, 100, ErrorMessage = "Value for {0} must be between {1} and {2}.")]

This means that if we enter a value outside of the range 0 to 100 inclusive then an execption will be raised when we validate in the code below:

Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "FranchiseDeductiblePercentage" });

Then in the textbox we specified a validation template as follows:

Validation.ErrorTemplate="{StaticResource validationTemplate}"

The definition of this template looks like:



<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">*</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>


Actually we don't need to overwrite the default validation template but it's interesting to see how this is done so that we have more control over what we are doing. But we want to get some kind of message that describes why the entered value is wrong. This is done by creating a trigger that sets a Tooltip when an error occurs. This is done as shown below:



<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>


Notice the very interesting binding path, in particular

Path=(Validation.Errors)[0].ErrorContent}

If I understand this correctly the view is listening for a validation error and then is able to resolve Validation.Errors. The display and range attributes set metadata that the Validation object is able to reflect upon and incorporate in a formatted error message. In our case we have 2 attributes

[Display(Name = "Franchise deductible percentage", Description = "Blanket Franchise deductible percentage used to bla bla")]
[Range(0, 100, ErrorMessage = "Value for {0} must be between {1} and {2}.")]

This will build a tooltip that looks like "Value for Franchise deductible percentage must be between 0 and 100."

Our next step was to think about adding a glyph or information icon that contains the description of the field so that the user has a more precise description of what the field is about. This turned out to be a little tricky. You could create some property that have the decription eg:



public double FranchiseDeductiblePercentageDescription
{
get
{
ValidationContext x = new ValidationContext(this, null, null) { MemberName = "FranchiseDeductiblePercentage" };
return x.DisplayName;
}



But this means a lot of extra typing. We found more information about the definition of the DisplayAttribute at:



http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute.aspx

We decided to use reflector to disassemble the Silverlight SDK to figure out how they implemented the DisplayViewer. The assembly was located in

C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\System.Windows.Controls.Data.Input.dll

There was a lot of code and we managed to get quite far but decided to refine our google search criteria to include the term converter. And we found an example but I can't remember from which web site. After some adjustments the viewer looks like




namespace Ccp.Wpf.Infrastructure.Converter
{
[ValueConversion(typeof(String), typeof(String))]
public class MetaDataDisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string result = "";

if (value != null)
{
Type sourceType = value.GetType();
var propertyName = parameter as string;

if (propertyName != null)
{
var propertyInfo = sourceType.GetProperty(propertyName);

foreach (DisplayAttribute attr in propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true))
{
result = attr.Description;
}
}
}

return result;
}


public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}


This converted is used in the following way:

<Image Source="/Ccp.Wpf.Infrastructure;Component/Images/Cute-Ball-Info-icon.png" Height="23" Stretch="Uniform" VerticalAlignment="Top"
ToolTip="{Binding Converter={StaticResource metaDataDisplayNameConverter}, ConverterParameter=FranchiseDeductiblePercentage}"/>

By not specifying a parameter in the Binding clause we pass the data context to the converter. The ConverterParameter specifies a string that we should search for when we make reflection. Reflection is a slow operation so we if we use this converter in large forms we should add a cache in the way of a static private property in the converter. This could be a Hashtable or a dictionary. Coming back to the validation mechanism. There are disadvantages to throwing exceptions in setters. These are described quite well by http://outcoldman.ru/en/blog/show/259

The main problem is that exceptions should only be thrown when an unforeseen condition is met. So we want to use data annotations without throwing exceptions in the setter. It turns out that these is another binding property called

ValidatesOnDataErrors=True

When this is set the view looks for an implementation of IDataErrorInfo. So this means instead of looking for an exception we are listening to an interface. This means that our binding looks like:



<TextBox Text="{Binding FranchiseDeductiblePercentage, Mode= TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=true}"
Width="100"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}" />


We found an implementation of IDataErrorInfo at

http://stackoverflow.com/questions/3739059/prism-idataerrorinfo-validation-with-dataannotation-on-viewmodel-entities

After some small modifications our implementation looked like:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Linq.Expressions;

namespace Ccp.Contracts.UI
{
public class DataErrorInfo : NotificationObject, IDataErrorInfo
{
public string Error
{
get
{
return null;
}
}
string IDataErrorInfo.this[string columnName]
{
get
{
return ValidateProperty(columnName);
}
}
protected virtual string ValidateProperty(string columnName)
{
// get cached property accessors
var propertyGetters = GetPropertyGetterLookups(GetType());
if (propertyGetters.ContainsKey(columnName))
{
// read value of given property
var value = propertyGetters[columnName](this);
// run validation
var results = new List();
var vc = new ValidationContext(this, null, null) { MemberName = columnName };
Validator.TryValidateProperty(value, vc, results);
// transpose results
var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);
return string.Join(Environment.NewLine, errors);
}
return string.Empty;
}
private static readonly Dictionary<string, object>
PropertyLookupCache = new Dictionary<string, object>();
private static Dictionary<string, Func<object, object>> GetPropertyGetterLookups(Type objType)
{
var key = objType.FullName ?? "";
if (!PropertyLookupCache.ContainsKey(key))
{
var o = objType.GetProperties().Where(p => GetValidations(p).Length != 0).ToDictionary(p => p.Name, CreatePropertyGetter);
PropertyLookupCache[key] = o;
return o;
}
return (Dictionary<string, Func>object, object>>)PropertyLookupCache[key];
}
private static Func<object, object> CreatePropertyGetter(PropertyInfo propertyInfo)
{
var instanceParameter = Expression.Parameter(typeof(object), "instance");
var expression = Expression.Lambda<Func<object, object>>(Expression.ConvertChecked(Expression.MakeMemberAccess(Expression.ConvertChecked(instanceParameter, propertyInfo.DeclaringType), propertyInfo), typeof(object)), instanceParameter);
var compiledExpression = expression.Compile(); return compiledExpression;
}
private static ValidationAttribute[] GetValidations(PropertyInfo property)
{
return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}
}
}


Notice that we inherit from our Notification object, this is so that we can use a func instead of a string to identify which property has changed. Then all we need to do to inherit from this base class in our view model. In our case we have an intermediate base class because we are implementing a MVVMC



namespace Ccp.Contracts.Model
{
///
/// This classes could be generated from a T4 template (pocos)
///

public abstract class ModelSettingsBase : DataErrorInfo
{
///
/// Just an example of a field
///

public string Name
{
get { return this.GetType().Name; }
}

public abstract void Save(int ModelRunId);
public abstract bool Validate();
}
}


Then



namespace Ccp.Wpf.Infrastructure.Entities
{
public class FranchiseStepPayouts : ModelSettingsBase
{
private double franchiseDeductiblePercentage;
[Display(Name = "Franchise deductible percentage", Description = "Blanket Franchize deductible percentage")]
[Range(0, 100, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public double FranchiseDeductiblePercentage
{
get
{
return franchiseDeductiblePercentage;
}
set
{
//Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "FranchiseDeductiblePercentage" });

franchiseDeductiblePercentage = value;
RaisePropertyChanged(() => FranchiseDeductiblePercentage);
}
}


And now it works without exceptions