Data Validation in Silverlight DataGrid
One of the features added to datagrid in Silverlight 3 Beta is Validation. This can be done
- Once we modify the content of a particular cell (cell validation)
- Once we modify the content of a particular datagrid row (would like to commit changes to row) (Row Validation)
In this blog, I will try to give an intro both cell validation and row validation by using examples.
Cell Validation
Cell Validation can be done in two ways
- Raising ValidationException in the property setter
- Using ValidationAttributes and calling ValidateProperty in property setter
Cell Validation by throwing ValidationException in the property setter
In order to understand this concept, let’s consider that our datagrid is populated with a collection of Employee (with the following properties) objects on which we want to do Cell Validation
public String firstName;
public String lastName;
public int age;
public String phone;
public String city;
public String state;
The validations that we want to do on these properties are
- Firstname, lastname, phone, city, state should not be null or empty
- Age has to be in the range 18 to 50
We define validation rules for these properties (in the setter properties) in the following way. The ValidationException (belonging to System.ComponentModel.DataAnnotations ) thrown is displayed as Cell Validation Error.
public string LastName
{
get { return lastName; }
set
{
if (value != lastName)
{
if (value == null || value == string.Empty)
{
throw new ValidationException("Last Name cannot be empty");
}
lastName = value;
NotifyPropertyChanged("LastName");
}
}
}
public int Age
{
get { return age; }
set
{
if (value != age)
{
if (value < 18 || value > 50 )
{
throw new ValidationException("Age should be between 18 and 50");
}
age = value;
NotifyPropertyChanged("Age");
}
}
}
Using Validation Attributes and calling ValidateProperty in property setter
The properties on which we want to do validation are decorated by validation attributes and we call the validationobject.validateproperty method in the setter of the property. This tells the validator to verify on the decorated validation attribute when validateProperty is called.
In the following code snippet, the Required, Range and RegularExpression validation attributes are used. The image below them shows the validation error when a validation rule is not met
[Required]
public string FirstName
{
get { return firstName; }
set
{
if (value != firstName)
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "FirstName" });
firstName = value;
NotifyPropertyChanged("FirstName");
}
}
}
[Required]
[Range(18,50)]
public int Age
{
get { return age; }
set
{
if (value != age)
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "Age" });
age = value;
NotifyPropertyChanged("Age");
}
}
}
[Required]
[RegularExpression("\\d{3}[-]\\d{3}[-]\\d{4}")] // (e.g - "999-999-1234")
public string Phone
{
get { return phone; }
set
{
if (value != phone)
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "Phone" });
phone = value;
NotifyPropertyChanged("Phone");
}
}
}
Row Validation
In addition to doing validation when we edit a particular cell, we can perform validation when we commit a particular row or entity in the datagrid. In order to do this,we decorate our BusinessObject class with the CustomValidation attribute that specifies the Validation class and the method that are used for validation. The row validation errors are shown at the bottom of the data grid in a error listbox. Clicking on an error in the listbox focuses on the cell that that has validation error. Resolving the validation error dynamically removes the error from the listbox.
In addition to specifying the CustomValidation attribute on the business object, we can also use the validation attributes on the Properties to show invalid entries as Row Validation errors.
[Required]
public string FirstName
{
get { return firstName; }
set
{
if (value != firstName)
{
firstName = value;
NotifyPropertyChanged("FirstName");
}
}
}
I will use the Employee class with the following properties as an example. Note that I also show the decorated validation attributes on the properties of Employee just for demo purpose (In actuality the property definition looks similar to the above).
[Required]
public string FirstName
[Required]
public string LastName
[Required]
public DateTime? BirthDate
[Required]
[Range(18,50)]
public int? Age
[Required]
public String City
[Required]
public String State
[Required]
[RegularExpression("\\d{3}[-]\\d{3}[-]\\d{4}")] // (e.g - "999-999-1234")
public string Phone
Please note that we are not explicity calling the Validator.ValidateObject method or throwing ValidationExceptions in the setter for invalid values (Doing so will show cell validation errors).
The business object Employee class is decorated with the following customvalidation attributes
[CustomValidation(typeof(EmployeeValidator), "IsValidBirthDay")]
[CustomValidation(typeof(EmployeeValidator), "IsValidBirthDayAge")]
public class Employee : INotifyPropertyChanged
The EmployeeValidator Class is static and we define validation methods inside it. The class looks like below for this particular example.
public static class EmployeeValidator
{
public static bool IsValidBirthDay(object employeeObject, ValidationContext context, out ValidationResult validationResult)
{
validationResult = null;
Employee employee = employeeObject as Employee;
DateTime date = employee.BirthDate.Value;
int dateComparison = date.CompareTo(DateTime.Today);
if (dateComparison > 1)
{
List<string> properties = new List<string>() { "BirthDate" };
validationResult = new ValidationResult("Birthday cannot be further than today!", properties);
}
return !(dateComparison > 1);
}
public static bool IsValidBirthDayAge(object employeeObject, ValidationContext context, out ValidationResult validationResult)
{
validationResult = null;
Employee employee = employeeObject as Employee;
DateTime date = employee.BirthDate.Value;
int age = DateTime.Today.Year - date.Year;
if (age != employee.Age)
{
List<string> properties = new List<string>() { "Age", "BirthDate" };
validationResult = new ValidationResult("Age does not match with Birthday! Your age should be " + age, properties);
}
return (age == employee.Age);
}
}
If you observe the IsValidBirthDay validation method above, this checks if the propertry “Birthdate” is not further from today. In the following code snippet
List<string> properties = new List<string>() { "BirthDate" };
validationResult = new ValidationResult("Birthday cannot be further than today!", properties);
we tell the validator that in this validationmethod, we are checking the property BirthDate only. We can check more than one property and associate them with a validation error. This is done for example in the IsValidBirthDayAge where the (Age,BirthDate) properties are checked for which the code which does this looks like below. When we click on the validation error in the listbox, we toggle between the properties (Age, BirthDate).
List<string> properties = new List<string>() { "Age", "BirthDate" };
validationResult = new ValidationResult("Age does not match with Birthday! Your age should be " + age, properties);
As you can see, developers can use these validation features that datagrid supports to develop compeling apps.
Comments
Anonymous
March 22, 2009
PingBack from http://blog.a-foton.ru/index.php/2009/03/22/data-validation-in-silverlight-datagrid/Anonymous
April 06, 2009
Hi Jeneate, please let me know the problem you are facing.Anonymous
April 06, 2009
Hi Jeneate, please let me know the problem you are facing.Anonymous
April 08, 2009
Grouping data in Silverlight DataGridAnonymous
April 08, 2009
I got this question on how do you add grouping to the DataGrid in Silverlight without using the RIA ServicesAnonymous
May 15, 2009
Why don't you attach the project to this article?Anonymous
July 14, 2009
Hi Satish, Nice article, All the ways of validations are collated It will be gerat if you add the code along with For the third method you have suggested 'Row Validation' Can you please add the code required to display the validation summery and also xamal code for same. I tried with first and second approach it works fine.But raising the exception on validation is not digesting thing. Can you give more details on third method.Anonymous
February 03, 2010
Can you share the code, that would be great!! Thanks in advance. Regards, Bala.Anonymous
February 10, 2010
Excellent article, helped me loads. I tried your code for the row validation but it didn't work. I kept getting the following error: Code: 4004 Category: ManagedRuntimeError Message: System.InvalidOperationException: The CustomValidationAttribute method 'IsValidBirthday' in type 'EmployeeValidator' must return System.ComponentModel.DataAnnotations.ValidationResult. Use System.ComponentModel.DataAnnotations.ValidationResult.Success to represent success. I think the problem might be because you used Silverlight Beta 3 and I am using the now official Silverlight 3 release. Anyway, to get this to work for me I had to alter the EmployeeValidator class as shown below. The static functions must return a ValidationResult not a bool. public static class EmployeeValidator { public static ValidationResult IsValidBirthDay(object employeeObject, ValidationContext context) { ValidationResult retval; Employee employee = employeeObject as Employee; DateTime date = employee.BirthDate.Value; int dateComparison = date.CompareTo(DateTime.Today); if (dateComparison > 1) { retval = new ValidationResult("Birthday cannot be further than today!", new List<string>() { "BirthDate" }); } else retval = ValidationResult.Success; return result } public static bool IsValidBirthDayAge(object employeeObject, ValidationContext context, out ValidationResult validationResult) { ValidationResult retval; Employee employee = employeeObject as Employee; DateTime date = employee.BirthDate.Value; int age = DateTime.Today.Year - date.Year; if (age != employee.Age) { retval = new ValidationResult("Age does not match with Birthday! Your age should be " + age, new List<string>() { "Age", "BirthDate" }); } else retval = ValidationResult.Success; return retval; } } Regards, Womb1eAnonymous
March 26, 2010
The comment has been removedAnonymous
November 13, 2013
Suppose there is Save Button Below Grid. If I Want to Save Information How to Handle Validations On save Button Click.... Data Grid Give Gives validation but It Completes Save Action Too... Help....Please...