Sunday, August 8, 2010

Input Validation using MVVM Pattern

In this post I will show you how you can validate user input using MVVM Pattern. I have continue my previous post regarding the MVVM, which is used to bind the user GUI with the ViewModel.Below are the two screen shot of my application which I have created for this post as you can see that I have used the previous post where I have user id, first name, last name, email address , contact number and the date of birth fields. The first screen shot is the normal one when user enter the valid inputs.

In the second screen shot you can see the border of the text box are red and the red asterisk  (*) sign is appearing at the right side of the text box which indicate some problem in the user input. and also when user place mouse over the text box tool tip will appear which will display the error message. I have only validate the text box not the date time picker to just give you an idea to how to validate the user input in MVVM pattern.

Now let us start with the code for this example code. If you have read my previous post you can see that I have model, view model placed in respective folder and the view which is in this case is only one form is places outside and I have place theme in the theme folder and the classes for the relay command and the view base( which is used to be base class for every window). 

        In the model class I have implemented IDataErrorInfo interface, the model class for the view model already implement INotifyPropertyChanged interface. Now I have second interface for the input validation by using the IDataErrorInfo interface which Provides the functionality to offer custom error information that a user interface can bind to, this interface has two members the Error property which is used to provided An error message indicating what is wrong with this object. The default is an empty string (""). And the Item which Gets the error message for the property with the given name. I have implemented Indexer in my code and error property is not implemented in my code. Below is my code which I have write in item property of the IDataErrorInfo interface property.

public string this[string columnName] { get { string strMessage = string.Empty; ValidateUserInput(ref strMessage, columnName); return strMessage; } } private void ValidateUserInput(ref string pstrMessage, string pstrColumnName) { switch (pstrColumnName) { case "UserID": if (string.IsNullOrEmpty(UserID)) pstrMessage = "User ID is required."; break; case "FirstName": if (string.IsNullOrEmpty(FirstName)) pstrMessage = "First name is required."; else if (string.IsNullOrWhiteSpace(FirstName) ) pstrMessage = "Spaces are not allowed in First name. only character are allowed"; else if(FirstName.Length <=2) pstrMessage = "First name lenght should be at least 2."; break; case "LastName": if (string.IsNullOrEmpty(LastName) ) pstrMessage = "Last name is required."; else if (string.IsNullOrWhiteSpace(LastName) ) pstrMessage = "Spaces are not allowed in Last name. only character are allowed"; break; case "ContactNumber": if (string.IsNullOrEmpty(ContactNumber)) pstrMessage = "Contact Number is required"; else if(Regex.IsMatch(ContactNumber ,@"^\d+$")==false ) pstrMessage = "Only digits are allowed in Contact Number field."; break; case "EmailID": if (string.IsNullOrEmpty(EmailID)) pstrMessage = "Email ID is required."; else if (Regex.IsMatch(EmailID, @"^[A-Za-z0-9_\-\.]+@(([A-Za-z0-9\-])+\.)+([A-Za-z\-])+$") == false) pstrMessage = "Please enter valid email ID."; break; } }

Here you can see that I have write separate function for the validation of the user input, and I have call user input validation function from the item property from where I have pass the message as reference parameter and the column name as normal parameter. Here you can see that based the column name parameter I have validate the properties, mean if User id then I have only check for the required , and for the first name I have some I have check for the required field , only character can be entered and also that length of the first name is at least 2 character, For the last name I have check for the required and also for the character, for contact number I have check for the required and expression to validate only the digits in the contact number and at the end to entered the email address and validate the email address entered by the user. This the my validation of the user input. Now lets see that to change on the binding sided mean in the view to validate the user input.

    In the view end I have added on property to the binding ValidatesOnDataErrors. I have set this property to true for the User id , first name, last name , contact number and the email address text box control as I want to validate these control.

< TextBox Grid.Row="1" Grid.Column="1" Margin="2,1,10,1" Text="{Binding UserID, Mode=TwoWay, ValidatesOnDataErrors=True}" Height="23"/>

Rest of the code in the view is same which you have see in my previous post related to the MVVM.

Error Template:
                        In the text box template I have added the ErrorTemplate in the style of the text box conlrol. Here you can see that AdornedElementPlaceholder is used which is placed in border of corner radius exactly same as the corner radius of the text box template element.

< Setter Property="Validation.ErrorTemplate"> < Setter.Value> < ControlTemplate> < Grid > < Grid.ColumnDefinitions > < ColumnDefinition Width="90*" /> < ColumnDefinition Width="20"/> < /Grid.ColumnDefinitions> < Border BorderBrush="Red" BorderThickness="1" CornerRadius="2.75" Grid.Column="0"> < AdornedElementPlaceholder Grid.Column="0"/> < /Border> < TextBlock Foreground="Red" Grid.Column="1" Margin="0" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="txtError">*</TextBlock> < /Grid> < /ControlTemplate> < /Setter.Value> < /Setter>

Then I have used the text block with red color and bold text to placed the asterisk (*) sign, when the error occurred the error template defined for the text box is used to show the error, if you don't specify your own error template the default will be used which placed red rectangle around the control.

And below is my code which is added in the text box template , so that when the Validation.HasError is true the tooltip is set for the respective error message.When user mouse over the error text box.

<Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" /> </Trigger>

Hope you get some idea of how to use the IDataErrorInfo interface when using the MVVM pattern to validate the user input from the view model or in the model of the view.You can download the source code from here

All and any comments / bugs / suggestions are welcomed!


Anonymous said...

Great tutorial. Thanks for sharing.

Xorch said...

What should I check in my CanExecuteCommand method if my textbox has error? So far error is showing but I don't know what to check in can execute to prevent from executing. Thanks for the answer.

Anonymous said...

Thanks for your post.. It is good and helped me a lot.. but when I implement your code. Bydefalt the textbox is showing in redborder when I open the window for the first time. The content in the textbox should validate after pressing tab(lost focus of control).