Data Validation in Silverlight

In this blog, I will show you three approaches to do data validation in Silverlight.

Sample Scenario:

Let's say I have a UI for users to enter their registration information for my website. When user input the email address in the UI below, I want to check if the format is valid or not. If not valid, we should give proper visual clues (error message) to help user to fix it.

image

 

Throw Exception in Property Setter

The idea of throwing exceptions from the property setters and reporting back to the UI was introduced in Silverlight 3. In this example, we will do the validation directly in the setter of the Email property. See the code snippet below.  

C#

public class User

{

    private static string EmailPattern = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";

    private string email;

    public string UserName

    {

        get;

        set;

    }

    public string Email

    {

        get

        {

            return email;

        }

        set

        {

            if (String.IsNullOrWhiteSpace(value))

            {

                throw new ArgumentException("Email address should not be empty.");

            }

            string input = value.Trim();

            if (!Regex.IsMatch(input, EmailPattern, RegexOptions.IgnoreCase))

            {

                throw new ArgumentException("Invalid email address format.");

            }

            this.email = input;

        }

    }

}

VB

Public Class User

    Private Shared EmailPattern As String = "^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"

    Private m_email As String

 

    Private _UserName As String

    Public Property UserName() As String

        Get

            Return _UserName

        End Get

        Set(ByVal value As String)

            _UserName = value

        End Set

    End Property

 

    Public Property Email() As String

        Get

            Return m_email

        End Get

        Set(ByVal value As String)

            If [String].IsNullOrWhiteSpace(value) Then

                Throw New ArgumentException("Email address should not be empty.")

            End If

            Dim input As String = value.Trim()

  If Not Regex.IsMatch(input, EmailPattern, RegexOptions.IgnoreCase) Then

                Throw New ArgumentException("Invalid email address format.")

            End If

            Me.m_email = input

        End Set

    End Property

End Class

 

In the Xaml, set the ValidatesOnExceptions property to true for the Email textbox. By setting it to true, the binding engine will catch all exceptions that are thrown when updating the source object using user input. If error occurs, the error message of the exception will be displayed in the UI. (Note: if you are using Drag-and-Drop Data Binding feature in Visual Studio, the following piece will be generated automatically)

<TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="emailTextBox" Text ="{ Binding Path =Email, Mode =TwoWay, ValidatesOnExceptions =true, NotifyOnValidationError =true}" VerticalAlignment="Center" Width="120" />

Hit F5 to run the application. You will see the following behavior:

image

image

 

 

Use Data Annotations

Starting from Silverlight 3, we can also use data annotations to do validations. First we need to add a reference to System.ComponentModel.DataAnnotations for the Silverlight client project.

Next, add Required and RegularExpression attribute to the Email property. And then call Validator  to validate the property in the setter.

C#

public class User

{

    public string UserName

    {

        get;

        set;

    }

    /// <summary>

    /// Email is a required field. It should be provided with valid format.

    /// </summary>

    private string email;

    [Required]

    [RegularExpression(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")]

    public string Email

    {

        get

        {

            return email;

        }

        set

        {

            Validator.ValidateProperty(value,

                new ValidationContext(this, null, null) { MemberName = "Email" });

            this.email = value;

        }

    }

}

VB

Public Class User

    Private _UserName As String

    Public Property UserName() As String

        Get

            Return _UserName

        End Get

        Set(ByVal value As String)

            _UserName = value

        End Set

    End Property

 

    ''' <summary>

    ''' Email is a required field. It should be provided with valid format.

    ''' </summary>

    Private m_email As String

    <Required()> _

    <RegularExpression("^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")> _

    Public Property Email() As String

        Get

            Return m_email

        End Get

        Set(ByVal value As String)

            Validator.ValidateProperty(value, New ValidationContext(Me, Nothing, Nothing))

            Me.m_email = value

        End Set

    End Property

End Class

In the Xaml, set the ValidatesOnExceptions property to true for the Email textbox.

<TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="emailTextBox" Text ="{ Binding Path =Email, Mode =TwoWay, ValidatesOnExceptions =true, NotifyOnValidationError =true}" VerticalAlignment="Center" Width="120" />

 

Hit F5 to run the application. You will see the similar behavior:

image

image

More information can be found at: https://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx

 

 

Implement IDataErrorInfo 

The IDataErrorInfo idea was first introduced in Windows Forms, and then added into WPF 3.5. Now it is available in Silverlight 4! In this example, we will make the User class to implement the IDataErrorInfo interface.

The Error property should provide an error message indicating what is wrong with this object. In this example, we just return null (or nothing in VB). In the Item property, we implement the logic to check the value for the specific column and return validation error message. See the code snippet below for the validation logic.

C#

public class User : System.ComponentModel.IDataErrorInfo

{

    private static string EmailPattern = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";

    public string UserName

    {

        get;

        set;

    }

    public string Email

    {

        get;

        set;

    }

    public string Error

    {

        get { return null; }

    }

    public string this[string columnName]

    {

        get

        {

            Debug.Assert(columnName != null, "columnName should not be null");

            if (columnName.Equals("Email", StringComparison.Ordinal))

            {

                if (String.IsNullOrWhiteSpace(this.Email))

                {

                    return "Email address should not be empty.";

                }

                if (!Regex.IsMatch(this.Email.Trim(), EmailPattern, RegexOptions.IgnoreCase))

                {

                    return "Invalid email address format.";

                }

            }

            return null;

        }

    }

}

VB

Public Class User

    Implements System.ComponentModel.IDataErrorInfo

    Private Shared EmailPattern As String = "^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"

 

    Private _UserName As String

    Public Property UserName() As String

        Get

        Return _UserName

        End Get

        Set(ByVal value As String)

            _UserName = value

        End Set

    End Property

 

    Private _Email As String

    Public Property Email() As String

        Get

            Return _Email

        End Get

        Set(ByVal value As String)

            _Email = value

        End Set

    End Property

 

    Public ReadOnly Property [Error]() As String

        Get

            Return Nothing

        End Get

    End Property

 

    Default Public ReadOnly Property Item(ByVal columnName As String) As String

        Get

            Debug.Assert(columnName IsNot Nothing, "columnName should not be null")

            If columnName.Equals("Email", StringComparison.Ordinal) Then

                If [String].IsNullOrWhiteSpace(Me.Email) Then

                    Return "Email address should not be empty."

                End If

                If Not Regex.IsMatch(Me.Email.Trim(), EmailPattern, RegexOptions.IgnoreCase) Then

                    Return "Invalid email address format."

                End If

            End If

            Return Nothing

        End Get

    End Property

End Class

 

In the Xaml, set the ValidatesOnErrors property to true instead of the ValidationOnExceptions.

<TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="emailTextBox" Text ="{ Binding Path =Email, Mode =TwoWay, ValidatesOnDataErrors =true, NotifyOnValidationError =true}" VerticalAlignment="Center" Width="120" />

Hit F5 to run the application. You will see the similar behavior:

image

image

 

Conclusion

Silverlight provides many different ways to do data validation, which is really handy when building business applications. With the power of Silverlight styles and control templates, we can go further to customize the way to display visual cues to the user.

Enjoy!