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");

                }

            }

        }

cellvalidation1

        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");

                }

            }

        }

 cellvalidation2

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");

                }

            }

        }

cellvalidation3

 

        [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");

                }

            }

        }

cellvalidation4

 

        [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");

                }

            }

        }

cellvalidation5 

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);

 

rowvalidation1

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);

rowvalidation2

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 DataGrid

  • Anonymous
    April 08, 2009
    I got this question on how do you add grouping to the DataGrid in Silverlight without using the RIA Services

  • Anonymous
    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, Womb1e

  • Anonymous
    March 26, 2010
    The comment has been removed

  • Anonymous
    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...