Browse by Tags

All Tags » idataerrorinfo   (RSS)

Sometimes I love how a little reflection can work magic. In this case I was building what I'll call a "non-intrusive" validation system. The project contains entities that are generated by templates, and it would be extremely difficult to crack open those templates to put in a lot of custom validation code. So I decided to approach it using a helper class that would attach to the generated entities.

As always, I work backwards. I like to have readable, fluent interfaces, so I decide what I want them to look like and then implement the classes and interfaces to make them work. In this case the entities implemented INotifyPropertyChanged and IDataErrorInfo. This makes it easy on the validation class because if it needs to validate a property, it can simply hook into the property change notification and pick up the new value for validation. Problem solved.

The validation mechanism ended up looking like this:

myClass.GetValidationHelper()
   .WithValidation<FieldIsRequired>(()=>MyField)
   .WithValidation<FieldIsInRange>(()=>MyField,0,999) 

So, how do we get there? I'm not going to give you all of the code but I'll touch on a few salient points. First, creating a common interface for validations makes it easy for the validation helper to parse through a dictionary (i.e. if property "x" comes in, run all validations for "x"). Implementing the validators with a constructor makes it easy to pass parameters. In the case of the FieldIsInRange there is actually a constructor that takes the minimum and maximum values. When you use reflection, you pass in parameters and it finds the matching constructor for you. This means I can have an extension method like this:

public static ValidationHelper WithValidation<TValidator, TProperty>(
   this ValidationHelper helper,
   Expression<Func<TProperty>> propertyExpression,
   params object[] args)
   where TValidator : IMyValidator

And I can simply pass the arguments through to reflection and it will match the appropriate constructor like this:

var validator = (TValidator)Activator
    .CreateInstance(typeof(TValidator), args);

The first parameter after the "extension" property of the helper is a property expression. You may find it is familiar because that's the guidance that Prism provides for strongly-typed property names and has been the topic of much debate. I settled on that format for my Jounce framework and while I initially used it to get property names, it actually comes in very handy for validation.

I won't rehash extracting the property name because it's been done. There are lots of debates over the relative performance of different methods. Here's a hint: for me, most of my property change notification is so I know when the user changes a field. Last time I checked, a user can't type fast enough to make my property changes fire so often that I'm really going to lose sleep over milliseconds here or there. I'd rather have something that is strongly typed so I don't have refactoring nightmares and put some of that plumbing in than have something that is convuluted to implement and code but saves me a tiny fraction of time that the user won't even notice.

You can see the source code in the ExtractPropertyName method by navigating to the BaseNotify class in Jounce - here is a direct link to the source for your viewing pleasure.

In the validation engine, we get an added benefit. What's passed in with this format:

CallSomeMethod(()=>myEntity.MyProperty);

Has a specific signature. It's an expression that contains a function that returns a property, and looks like this: Expression<Func<TProperty>>. Pretty cool. So we can walk the expression tree and get the property name. Once the property change notification fires, we can inspect the arguments for the event and see the property name, and if it is one we want to validate it, we go ahead and perform the validation. But of course, to validate it, we need to get the value. How do we do it?

Often I see the solution og taking the property name that's been extracted from the expression and then using reflection to find the getter and invoking it, like this:

var value = (TProperty) typeof(T)
                  .GetProperty(propertyName)
                  .GetGetMethod()
                  .Invoke( instance, null );

That will work, but it's like tying your arms into a pretzel and reaching around your back to scratch your nose. Doesn't really make sense when you've got the expression already — what was passed in? It's a function that resolves the value! So we use some expression magic to run the lambda expression.

Given the propertyExpression passed in with the signature Expression<Func<TProperty>>, all you have to do is compile the body of the expression. The following code will extract the value, first by compiling the function to a lambda expression, then by calling the lambda expression:

var lambdaExpression = Expression.Lambda<Func<TProperty>>(
   propertyExpression.Body).Compile(); 
var value = (TProperty) lambdaBLOCKED EXPRESSION; 

Now you can pass that value (which was resolved to the most current value of the property, without using reflection) into the validation routines. One of those validation routines is a generic routine to compare values. Why write this multiple times when the framework provides a nice IComparable interface for things that can be compared? The framework I built works with strings, so my first challenge was parsing the strings to the target type before comparing them. This is where we can have some fun.

Here is the code to parse any string to any type ... provided the type supports the TryParse method. First, you get the type and you create a variable to hold the output of the parse call:

public static bool TryParse(string input, out T value) 
{
   var type = typeof(T); 
   value = default(T); 
   ...
}

Next, you build the parameters to the call. You want the method with the signature that takes a string type for the first parameter, and a reference to the type for the output parameter. Here's where it is important to know your C# and your CLR. The "out" keyword is enforced by the compiler, and is a language construct. The framework knows nothing about it. To the framework, it's simply a reference type. You might not know that you can create special types that are tagged as "reference" - read the documentation here for more information. If you suffix a type name with an ampersand (&) you are flagging that type to be passed by reference.

So, here's the rest of the method down to the call:

var parameters = new[]
{
   typeof(string),
   System.Type.GetType(string.Format("{0}&", type.FullName))
};

var method = type.GetMethod(
   "TryParse",
   parameters);

if (method == null)
{
   return false;
}

var invokeParameters = new object[] { input, value };
var returnValue = (bool) method.Invoke(null, invokeParameters);

Now comes just a slight trick that will cause problems if you're not aware of it. You tagged the parameter as a reference type, but most of the time your comparisons will be dealing with value types (integers, decimals, etc.) so what happens in the call is that the types are boxed. It's a common mistake to make that call above and assume your value will now be populated if the returnValue is true. Imagine your frustration when you run this and it is always set to the default value instead! What gives? It turns out the call does update the reference — but not the one you provided. It updates the reference in the array you passed in!

The last step in the global TryParse method is therefore to pull that value back out and return the result:

public static bool TryParse(string input, out T value) 
{
   ...
   if (returnValue)
   {
      value = (T)invokeParameters[1];
   }

   return returnValue;
}

And there you have it ... the generic try parse. Now if you wan to compare the values, you can cast to IComparable and simply use:

public int ParseAndCompare(string valueOne, string valueTwo) 
   where T: IComparable 
{
   T parsedOne, parsedTwo;
   if (TryParse(valueOne, out parsedOne) && 
       TryParse(valueTwo, out parsedTwo))
   {
       return parsedOne.CompareTo(parsedTwo);
   }

   throw new ArgumentException("One of the values didn't parse.");
}

I will admit the details of the implementation can get a little bit complex if you're not familiar with expression trees or reflection. However, the beauty of this approach is that the core pieces are not likely to change much as the basic infrastructure to build the validations on top of. The payoff is enormous, because now a developer can write a new validation by implementing a simple interface and then add the validation using the fluent syntax I showed earlier. That makes the code both readable and easy to maintain.

Jeremy Likness

Silverlight 4 provides some very powerful data form capabilities out of the box. You will no longer have to throw an exception just to send a validation error or even worry about hard-coding labels. With support for data annotations, IDataErrorInfo and more, you now have plenty "out of the box" to work with.

Let's explore the surface of some of these powerful new features!

IDataErrorInfo

With support for IDataErrorInfo as well as INotifyDataErrorInfo, you no longer have to raise exceptions just to validate your classes. Fire up VS 2010 and create a new Silverlight 4 application. First, we'll create a "helper" class to derive our entities from in order to perform validation and notify property changes. The class looks like this:


public abstract class BaseEntity : INotifyPropertyChanged, IDataErrorInfo
{
    private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

    protected void RemoveErrors(string prop)
    {
        if (_errors.ContainsKey(prop))
        {
            _errors.Remove(prop);
        }
    }

    protected void AddError(string prop, string error)
    {
        if (_errors.ContainsKey(prop))
        {
            _errors[prop].Add(error);
        }
        else
        {
            _errors.Add(prop, new List<string> { error });
        }
    }

    protected void OnPropertyChanged(string prop)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(prop));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual string Error
    {
        get { return string.Empty; }
    }

    public virtual string this[string columnName]
    {
        get 
        {
            System.Text.StringBuilder retVal = new System.Text.StringBuilder();

            if (_errors.ContainsKey(columnName))
            {
                bool first = true;
                foreach (string error in _errors[columnName])
                {
                    retVal.Append(error);
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        retVal.Append(Environment.NewLine);
                    }
                }
            }

            return retVal.ToString();
        }
    }
}

There are a few things going on here. First, we implement INotifyPropertyChanged in to facilitate binding. The protected OnPropertyChanged method allows derived classes to hook into this event.

Next, we implement IDataErrorInfo. This forces us to provide a single error property as well as an extension method that references errors for a given property. John Papa has an excellent article about building a handler for this at Enabling Validation in Silverlight 4 with IDataErrorInfo. In my case, I keep a dictionary referenced by the property name that holds a list in case there are multiple errors I want to show. When the errors are requested, I fold them into a single string using Environment.NewLine (OK, so the beta just came out the other day ... can't say I've fully tested that part).

Data Annotations

Now you can build a PersonInfo class based on this base class. To keep things simple for a contrived example, I chose just a first name, last name, and age. Here we'll need to add a reference to System.ComponentModel.DataAnnotations. You must install the latest Silverlight Toolkit for this. If you need to browse to it manually, it's located under the toolkit installation directory (usually c:\Program Files\Microsoft SDKs\Silverlight) under v4.0\Libraries\Client. Now we can derive the class and annotate it, like this:


public class PersonInfo : BaseEntity 
{
    private string _firstName, _lastName;

    private int _age;

    [Display(Name="First Name")]
    [Required]
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (string.IsNullOrEmpty(value.Trim()))
            {
                AddError("FirstName", "First name is required.");
            }
            else
            {
                RemoveErrors("FirstName");
                if (!value.Equals(_firstName))
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                }
            }
        }
    }

    [Display(Name="Last Name")]
    [Required]
    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (string.IsNullOrEmpty(value.Trim()))
            {
                AddError("LastName", "Last name is required.");
            }
            else
            {
                RemoveErrors("LastName");
                if (!value.Equals(_lastName))
                {
                    _lastName = value;
                    OnPropertyChanged("LastName");
                }
            }
        }
    }

    [Display(Name="Age (Years)")]
    [Required]
    public int Age
    {
        get { return _age; }
        set
        {
            if (value  130)
            {
                AddError("Age", "Age must be a valid integer between 18 and 130.");
            }
            else
            {
                RemoveErrors("Age");
                if (!value.Equals(_age))
                {
                    _age = value;
                    OnPropertyChanged("Age");
                }
            }
        }
    }
}

You'll probably pull out validations into a handler / rule set but basically what I'm doing is validating the value, setting an error or clearing all errors if it passes, then calling the property changed event if the value truly changes. Note the use of annotations to specify required properties as well as "friendly names" for the properties.

Now that our class is prepped, we can get to work on the XAML.

Implicit Styles

With support for implicit styles, we can set a style based on a target type and style it that way - allowing for themes to be set externally. We'll take advantage of that to set the width for our TextBox controls. I also add a reference to the PersonInfo class to use in data binding, so I won't need to touch the code-behind for MainPage.xaml:


<UserControl.Resources>
   <local:PersonInfo x:Key="Person"/>
   <Style TargetType="TextBox">
      <Setter Property="Width" Value="200"/>
   </Style>
</UserControl.Resources>

Data Input Helpers

Next, add a reference to System.Windows.Controls.Data.Input (found in the toolkit as well, same folder as the data annotations). We'll reference it at the top of our XAML (along with the local reference we need for the PersonInfo class:


xmlns:local="clr-namespace:DataForm"
    xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"

Great, now let's get to work! I'm going to show you the full snippet of XAML and then explain the pieces:


    <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource Person}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>           
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <dataInput:Label Target="{Binding ElementName=tbFirstName}"
                         Grid.Row="0" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="0" Grid.Column="1">
            <TextBox x:Name="tbFirstName" 
                 Text="{Binding Path=FirstName, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue=''}"/>
            <dataInput:DescriptionViewer Description="Please enter your first name."
                                         Target="{Binding ElementName=tbFirstName}"/>
        </StackPanel>
        <dataInput:Label Target="{Binding ElementName=tbLastName}"
                         Grid.Row="1" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="1" Grid.Column="1">
            <TextBox x:Name="tbLastName" 
                 Text="{Binding Path=LastName, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue=''}" />
            <dataInput:DescriptionViewer Description="Please enter your last name."
                                         Target="{Binding ElementName=tbLastName}"/>
        </StackPanel>
        <dataInput:Label Target="{Binding ElementName=tbAge}"
                         Grid.Row="2" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" 
                    Grid.Row="2" Grid.Column="1">
            <TextBox x:Name="tbAge" 
                     Text="{Binding Path=Age, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, FallbackValue='0'}"/>
            <dataInput:DescriptionViewer Description="Please enter a valid age between 18 and 130."
                                         Target="{Binding ElementName=tbAge}"/>
        </StackPanel>
        <dataInput:ValidationSummary Grid.Row="3" Grid.Column="0"
                                     Grid.ColumnSpan="2"/>        
    </Grid>

Some of these helper classes are made available via the toolkit for Silverlight 3 as well.

Labels

The label class lets us bind to an element like a textbox. It will pull the name of the databound property from the annotations and display it. If a validation error is thrown, it will turn red to further reinforce the error. If the field is tagged as required, the label will automatically bold itself to indicate a required field.

Learn more about the Label class

New Bindings

If you take a look at the textbox contorls, you'll see we've added new binding parameters. Specifically, ValidatesOnDataErrors (as opposed to exceptions) and FallbackValue (a value to use when databinding fails).

Learn more about bindings

Description Viewer

The description viewer control provides an easy way to show tips and hints and is similar to the ToolTip service. It will show an informational icon that, when the cursor hovers over, will provide a hint or description.

Learn more about the description viewer

Validation Summary

Finally, we add a validation summary control. The default behavior is to list all errors. When the user clicks on an error, it gives focus to the UI element that caused the error.

Learn more about the validation summary class

Putting it all Together

When you tie this all together and run it (without any additional code behind), this is an example of what you'll see:

Silverlight 4 Beta Data Forms

  • Note the labels automatically pulled from the DisplayAttribute on the entity class
  • The labels are bold due to the RequiredAttribute annotation
  • The labels on properties with errors are colored red
  • I clicked on the information glyph next to the first name text box and was the hint to enter my first name
  • I clicked on the red triangle in the upper right corner of the text box for age and was shown the error that my age is not in the valid range
  • The ValidationSummary control has summarized my errors (if I click on one, I'll be taken to the control with the issue)

As you can see, that's quite a bit of functionality right out of the box, and it allows a nice, clean separation of things like attributes and descriptions from the user interface that presents them.

Jeremy Likness