Introduction
The goal here is to create an easy way to add validation to properties when working with xaml and MVVM.
I have chosen this approach:
[ValidateMethod("ValidateFirstName")] public string FirstName { get { return firstName; } set { SetProperty(ref firstName, value); } } public string ValidateFirstName() { if (string.IsNullOrEmpty(FirstName)) { return "First name is mandatory"; } return string.Empty; }
Simple attribute referring to a method by string. The method then handles the validation.
The reason for choosing attributes is to make validation on properties easy to spot for all developers reading the code.
ValidateMethodAttribute
Frist thing needed for this to work is the attribute. Made it as simple as possible.
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class ValidateMethodAttribute : Attribute { private readonly string methodName; public ValidateMethodAttribute(string methodName) { this.methodName = methodName; } public string MethodName { get { return methodName; } } }
Validator
Next thing is a class witch can find the attribute above a property, invoke the method and return the result of the method.
For this, I made a validator class and the following method.
private static string ValidateMethod(object instance, PropertyInfo property) { var vma = ReflectionHelper.GetAttribute<ValidateMethodAttribute>(property); if (vma != null) { return ReflectionHelper.InvokeMethod<string>(instance, vma.MethodName); } else { return string.Empty; } }
The method has two parameters.
- Instance witch should be an instance of the class holding the property and the validation method.
- Property witch is the property info for the property with attribute.
With reflection, it is now possible to get the attribute, by that get the method name, and invoke it. To make the code more readable I have wrapped the reflection part into a helper class. You can see the ReflectionHelper class in the source code.
Now calling with PropertyInfo as an argument will not really be easy to call for a ViewModel. Therefore, I have created additional two methods. One for validating a specific property and one for validating all properties on the instance.
public static string ValidateProperty(object instance, string propertyName) { var property = ReflectionHelper.GetPropertyInfo(instance, propertyName); return ValidateMethod(instance, property); }
The propertyName makes great sense as the ViewModel already uses this approach for INotifyPropertyChanged. All we need to do is get the property info for that property.
public static List<string> ValidateAllProperties(object instance) { List<string> result = new List<string>(); foreach (PropertyInfo property in ReflectionHelper.GetPropertyInfos(instance)) { var errorString = ValidateMethod(instance, property); if (!string.IsNullOrEmpty(errorString)) { result.Add(errorString); } } return result; }
When validating all properties I simply use reflection to get all properties for the instance. Remember that the ValidateMethod checks if a property even has the attribute. Afterwards I look at the response and if it contains an error, I add it to the result.
INotifyDataErrorInfo
Time to make the code UI friendly. For this, I have chosen INotifyDataErrorInfo interface.
WPF supports INotifyDataErrorInfo out of the box. WindowsPhone Silverlight supports this to some degree. In my next blog post, I will show how make Universal apps (Windows store apps) support this interface.
In MVVM pattern a ViewModelBase is quite common and I am going to implement the interface in this class.
Let us look at the interface.
public interface INotifyDataErrorInfo { bool HasErrors { get; } event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; IEnumerable GetErrors(string propertyName); }
- HasErrors
- Simply to return whether or not the class in this case the view model has any errors.
- ErrorsChanged
- To tell when errors changes. Now clearly with this event we got the possibility to support async validation. I am not going to support this in my implementation.
- GetErrros
- Should return errors for a given property.
Simple enough now let us see how to implement the interface.
public bool HasErrors { get { return Validator.ValidateAllProperties(this).Any(); } }
Since I already have made a method witch can return all errors for an instance. I can just call this and ask if the list I got in return is empty.
protected void NotifyErrorsChanged(string propertyName) { var handler = ErrorsChanged; if (handler != null) { handler(this, new DataErrorsChangedEventArgs(propertyName)); } }
Similar to implementing INotifyPropertyChanged I have made a method witch can fire the event. In this case, I call this method every time a property have changed since my errors could potentially have changed as well.
public IEnumerable GetErrors(string propertyName) { var errorString = Validator.ValidateProperty(this, propertyName); if (!string.IsNullOrEmpty(errorString)) { return new List<string> { errorString }; } return null; }
Again, the validator does most of the work but the validator only returns a string. In case there is an error, I wrap it up in a list and return it.
Client
Great I am finally ready for validating. In the demo project, I have added a PersonViewModel as well and set it as DataContext. Once this is done I just need to write this xaml to bind to a property with validation.
<TextBox Text="{Binding FirstName}" />
By default validation is on. Should you need to “turn off” the validation for a binding. Simply set ValidatesOnNotifyDataErrors to false.
<TextBox Text="{Binding FirstName, ValidatesOnNotifyDataErrors=False}" />
Now as I mentioned earlier Windows Phone Silverlight supports this to some degree. The reason is; there is no default visuals for controls being invalid.
You can download the demo project here.
P.S Make sure you follow me on twitter @danielvistisen for updates on new posts.
Your sample does not have an adorner. There is no error message at all.
Hi Nandy.
Thanks for replying on my blog.
I assume you’re talking about the error text provided by the validation method and not a compiler error message.
The purpose of the post was not to show how to display errors but simply a way of handling errors with attributes and methods.
However, I probably should have added this to my sample. I’ll update my post later and describe how to do this. Meanwhile in case you don’t already know this and for future readers. If you replace the LastName TextBox with the code below. The error message will end up below the TextBox.
<TextBox
Text=”{Binding LastName, UpdateSourceTrigger=PropertyChanged}”>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<TextBlock
Text=”{Binding [0].ErrorContent}”
Foreground=”Red” />
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
As I mentioned I will but this in a blog post later and explain in more details how this work. Thanks again for pointing this out.
I updated your sample with the additional xaml code and it’s working now. Thanks.
I hope you will also add how to retrieve all errors from from the properties at once, say errors from both firstname and lastname and display these as a list.