Validating Data in Silverlight 4 Applications – IDataErrorInfo
Data validation is an important part of any application. If you've written code to validate data in Silverlight applications prior to the release of Silverlight 4, you've probably discovered that there isn't a great way to ensure that data is entered correctly and errors displayed properly by controls. The typical technique used for validating data was to throw exceptions within property setter blocks as data was found to be invalid. Controls bound to a property could be notified of a data validation exception by setting ValidatesOnExceptions to true in the data binding. Although this technique certainly works, there are some new options available in Silverlight 4 that can be used without resorting to throwing exceptions.
With the release of Silverlight 4 two new interfaces are now available including IDataErrorInfo and INotifyDataErrorInfo. In this post I'll walk through the process of using the IDataErrorInfo interface which provides a more simplistic way to validate data and provide end users with notifications. Let's start by taking a look at the interface's members:
public interface IDataErrorInfo { string this[string columnName] { get; } string Error { get; } }
The IDataErrorInfo interface only contains two members including Error which can return error information about the overall object and a default indexer property that can be used to write validation logic for different properties within an object. You'll implement the IDataErrorInfo interface on a client-side entity class in your Silverlight project such as the Person class shown next:
public class Person : INotifyPropertyChanged, IDataErrorInfo { public string this[string propertyName] { get { …. } } public string Error { get { …. } } }
In situations where the Error property isn't used (which is often the case), you can simply return null:
public string Error { get { return null; } }
However, it can be used to return an error message detailing what's wrong with the overall object as well:
string _Errors; const string _ErrorsText = "Error in Person data."; public string Error { get { return _Errors; } }
The bulk of validation work is done in the default indexer property. It's called by controls that are bound to object properties as data changes to see if a particular bound property is valid or not. The Person class above has two basic properties in it including Name and Age that need to be validated. This can be done in the default indexer property added into the class as a result of implementing IDataErrorInfo as shown next:
public string this[string propertyName] { get { _Errors = null; switch(propertyName) { case "Name": if (string.IsNullOrEmpty(Name)) { _Errors = _ErrorsText; return "Name cannot be empty!"; } break; case "Age": if (Age < 1) { _Errors = _ErrorsText; return "Age must be greater than zero."; } break; } return null; } }
The validation operations performed in the default indexer property are quite simple in this example. First, a switch statement checks which property needs to be validated. From there, each case statement is used to perform validation logic including checking that the Name property isn't null or empty and that the Age property has a value greater than 0. What's nice about this approach is that the validation logic is put in one place and in more real-world situations the logic can even be moved to an external validation object that provides some re-useable methods to check for null strings, check string lengths, validate numeric ranges, etc.
The end result of implementing the IDataErrorInfo interface is that controls bound to properties can check to see if data is valid or not and then display an error message as appropriate. This is done by adding the ValidatesOnDataErrors property to the data binding as shown next:
<TextBox Text="{Binding Name,Mode=TwoWay,ValidatesOnDataErrors=true}" Height="23" Width="120" HorizontalAlignment="Left" VerticalAlignment="Top" />
The following image shows an example of a TextBox control bound to a property containing invalid data. The error message displayed in red is generated automatically as the control checks the default indexer property from IDataErrorInfo of the bound object to verify if a given property is valid or not. This type of validation functionality exists in many controls throughout Silverlight such as TextBox, DataForm, DataGrid, etc.
The downside of the IDataErrorInfo interface is that it doesn't provide a way to perform asynchronous validation. In some situations you may need to call back to a server to check that a user ID or email address is unique within the system or that a tax code matches an existing code stored in a database. IDataErrorInfo doesn't support this type of scenario directly. In the second part of this series on validation I'll provide information about the INotifyDataErrorInfo interface which supports asynchronous data validation. If you want to get a jump on the process of implementing INotifyDataErrorInfo check out the associated code available with this post.
If you or your company is interested in training, consulting or mentoring on Silverlight 4 or other .NET technologies please visit http://www.thewahlingroup.com for more information. We’ve provided training, consulting and mentoring services to some of the largest companies in the world and would enjoy sharing our knowledge and real-world lessons learned with you.