Saturday, May 25, 2013

Validation in WPF

This example shows how to use an ErrorTemplate and a style trigger to provide visual feedback to inform the user when an invalid value is entered, based on a custom validation rule.

Download

Step 1: Create a WPF Application named ValidationInWPF and create following file structure.


Step 2: Update the xaml of MainWindow as follows. 




<Window x:Class="ValidationInWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:model="clr-namespace:ValidationInWPF.Model"
        xmlns:validationRules="clr-namespace:ValidationInWPF.ValidationRules"
        Title="MainWindow" 
        Height="243" 
        Width="652">

    <Window.DataContext>
        <model:Person/>
    </Window.DataContext>

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary 
                  Source="Style.xaml">
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="User Id:"/>
        <Label Grid.Column="0" Grid.Row="1" Content="User Name:"/>
        <Label Grid.Column="0" Grid.Row="2" Content="Email:"/>
        <Label Grid.Column="0" Grid.Row="3" Content="Contact # (xxx-xxx-xxxx):"/>
        <Label Grid.Column="0" Grid.Row="4" Content="Date of Birth"/>

        <TextBox Grid.Column="1" Grid.Row="0" Name="txtUserId" Width="150" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
            <TextBox.Text>
                <Binding Path="UserID" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" Mode="TwoWay">
                    <Binding.ValidationRules>
                        <validationRules:RequiredFieldValidation/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <TextBox Grid.Column="1" Grid.Row="1" Name="txtUserName" Width="150" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
            <TextBox.Text>
                <Binding Path="Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" Mode="TwoWay">
                    <Binding.ValidationRules>
                        <validationRules:RequiredFieldValidation/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <TextBox Grid.Column="1" Grid.Row="2" Name="txtEmail" Width="250" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top"  Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
            <TextBox.Text>
                <Binding Path="EmailID" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" Mode="TwoWay">
                    <Binding.ValidationRules>
                        <validationRules:ValidateEmailAddress/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <TextBox Grid.Column="1" Grid.Row="3" Name="txtContactNo" Width="150" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
            <TextBox.Text>
                <Binding Path="ContactNumber" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" Mode="TwoWay">
                    <Binding.ValidationRules>
                        <validationRules:ValidateContactNumber/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <DatePicker Grid.Column="1" Grid.Row="4" Name="dpDate" Width="150" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
            <DatePicker.Text>
                <Binding Path="DateOfBirth" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" Mode="TwoWay">
                    <Binding.ValidationRules>
                        <validationRules:ValidateAge Min="18" Max="120"/>
                    </Binding.ValidationRules>
                </Binding>
            </DatePicker.Text>
        </DatePicker>

            <Button Grid.Column="2" Grid.Row="5" Name="btnSave" Content="Save" Width="100" Height="25" HorizontalAlignment="Right" Click="btnSave_Click">

        </Button>

    </Grid>
</Window>

Step 3: Update the MainWindow.cs as follows.


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace ValidationInWPF
{
    //********************************************************************************************************************************
    //                                                  Class:MainWindow
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2013      | Kaushik Kumar Mistry | This Class is used to call the Interaction logic for MainWindow. 
    //********************************************************************************************************************************

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.dpDate.SelectedDate = DateTime.Now.AddYears(-20);
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            if (!ValidateView())
                MessageBox.Show("Validation failed !");
        }

        /// <summary>
        /// This method is used to validate view.
        /// </summary>
        /// <returns></returns>
        /// <Developer>Kaushik Kumar Mistry</Developer>
        /// <DateCreated>25 May 2013</DateCreated>
        /// <ModifiedBy>...</ModifiedBy>
        /// <ModifiedDate>...</ModifiedDate>
        private Boolean ValidateView()
        {
            BindingExpression bindingExpression;

            bindingExpression = txtUserId.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                if (bindingExpression.HasError)
                    return false;
            }

            bindingExpression = txtUserName.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                if (bindingExpression.HasError)
                    return false;
            }

            bindingExpression = txtContactNo.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                if (bindingExpression.HasError)
                    return false;
            }

            bindingExpression = dpDate.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                if (bindingExpression.HasError)
                    return false;
            }

            bindingExpression = txtEmail.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                if (bindingExpression.HasError)
                    return false;
            }

            return true;
        }
    }
}


*********************************************************

Step 4: Update the Person.cs as follows.

using System;
using System.ComponentModel;

namespace ValidationInWPF.Model
{

    //********************************************************************************************************************************
    //                                                  Class:Person
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2012      | Kaushik Kuamr Mistry | This Class is used to call the Interaction logic for Person. 
    //*********************************************************************************************************************************

    public class Person : System.ComponentModel.INotifyPropertyChanged
    {
        #region " Data Member "

        private String name;
        private String strEmailAddress;
        private String strContactNumber;

        #endregion

        /// <summary>
        /// Get or Set the User Id. 
        /// </summary>
        public string UserID { get; set; }

        /// <summary>
        /// Get or Set the Name. 
        /// </summary>
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
                RaisePropertyChanged("Name");
            }
        }
        
        /// <summary>
        /// Get or Set the Email Id.
        /// </summary>
        public String EmailID
        {
            get
            {
                return this.strEmailAddress;
            }
            set
            {
                this.strEmailAddress = value;
                RaisePropertyChanged("EmailID");
            }
        }

        /// <summary>
        /// Get or Set the Contact Number.
        /// </summary>
        public String ContactNumber
        {
            get 
            { 
                return this.strContactNumber; 
            }
            set
            {
                this.strContactNumber = value;
                RaisePropertyChanged("ContactNumber");
            }
        }

        /// <summary>
        /// Get or Set the Date of Birth.
        /// </summary>
        public DateTime DateOfBirth { get; set; }

        #region " INotifyPropertyChanged Implementation "

        /// <summary>
        /// PropertyChanged Event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// RaisePropertyChanged Function.
        /// </summary>
        /// <param name="propertyName"></param>
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

        }

        #endregion

    }
}

*********************************************************

Step 5: Update the RequiredFieldValidation.cs as follows.

using System;
using System.Windows.Controls;

namespace ValidationInWPF.ValidationRules
{

    //********************************************************************************************************************************
    //                                                  Class:RequiredFieldValidation
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2013      | Kaushik Kumar Mistry | This Class is used to call the Interaction logic for RequiredFieldValidation. 
    //********************************************************************************************************************************

    public class RequiredFieldValidation : ValidationRule
    {

        /// <summary>
        /// Implementation of required field validation. 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cultureInfo"></param>
        /// <returns></returns>
        /// <Developer>Kaushik Kumar Mistry</Developer>
        /// <DateCreated>25 May 2013</DateCreated>
        /// <ModifiedBy>...</ModifiedBy>
        /// <ModifiedDate>...</ModifiedDate>
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            String strValue = value as String;

            if (String.IsNullOrEmpty(strValue))
                return new ValidationResult(false, "* Required.");

            return new ValidationResult(true, null);
        }
    }
}

*********************************************************

Step 6: Update the ValidateAge.cs as follows.

using System;
using System.Windows.Controls;

namespace ValidationInWPF.ValidationRules
{

    //********************************************************************************************************************************
    //                                                  Class:ValidateAge
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2013      | Kaushik Kumar Mistry | This Class is used to call the Interaction logic for ValidateAge. 
    //********************************************************************************************************************************

    public class ValidateAge : ValidationRule
    {
        private int _min;
        private int _max;

        /// <summary>
        /// Get or Set the min age.
        /// </summary>
        public int Min
        {
            get { return _min; }
            set { _min = value; }
        }

        /// <summary>
        /// Get or Set the max age.
        /// </summary>
        public int Max
        {
            get { return _max; }
            set { _max = value; }
        }

        /// <summary>
        /// Implementation of age validation.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cultureInfo"></param>
        /// <returns></returns>
        /// <Developer>Kaushik Kumar Mistry</Developer>
        /// <DateCreated>25 May 2013</DateCreated>
        /// <ModifiedBy>...</ModifiedBy>
        /// <ModifiedDate>...</ModifiedDate>
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            try
            {
                DateTime birthday = Convert.ToDateTime(value);
                DateTime now = DateTime.Today;
                int age = now.Year - birthday.Year;

                if (now < birthday.AddYears(age))
                    age--;

                if ((age < Min) || (age > Max))
                {
                    return new ValidationResult(false,"Please enter an age in the range: " + Min + " - " + Max + ".");
                }
                else
                    return new ValidationResult(true, null);
            }
            catch
            {
                return new ValidationResult(false, "Date is not in correct format.");
            }
        }
    }
}

*********************************************************

Step 7: Update the ValidateContactNumber.cs as follows.

using System;
using System.Text.RegularExpressions;
using System.Windows.Controls;

namespace ValidationInWPF.ValidationRules
{

    //********************************************************************************************************************************
    //                                                  Class:ValidateContactNumber
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2013      | Kaushik Kumar Mistry | This Class is used to call the Interaction logic for ValidateContactNumber. 
    //********************************************************************************************************************************

    public class ValidateContactNumber : ValidationRule
    {
        /// <summary>
        /// Implementation of phone number validation.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cultureInfo"></param>
        /// <returns></returns>
        /// <Developer>Kaushik Kumar Mistry</Developer>
        /// <DateCreated>25 May 2013</DateCreated>
        /// <ModifiedBy>...</ModifiedBy>
        /// <ModifiedDate>...</ModifiedDate>
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            String strContactNumber = value as String;

            if (String.IsNullOrEmpty(strContactNumber))
                return new ValidationResult(true, null);

            else if (Regex.IsMatch(strContactNumber, @"^\d{3}-?\d{3}-?\d{4}$") == false)
                return new ValidationResult(false, "Invalid Phone number.");

            return new ValidationResult(true, null);
        }
    }
}

*********************************************************

Step 8: Update the ValidateEmailAddress.cs as follows.

using System;
using System.Text.RegularExpressions;
using System.Windows.Controls;

namespace ValidationInWPF.ValidationRules
{

    //********************************************************************************************************************************
    //                                                  Class:ValidateEmailAddress
    //********************************************************************************************************************************
    //                                     M O D I F I C A T I O N   M I L E S T O N E S
    // -------------------------------------------------------------------------------------------------------------------------------
    //  Date             |  Created By          | Description 
    // --------+-------------------+--------------------------------------------------------------------------------------------------
    // May 25, 2013      | Kaushik Kumar Mistry | This Class is used to call the Interaction logic for ValidateEmailAddress. 
    //********************************************************************************************************************************

    public class ValidateEmailAddress : ValidationRule
    {
        /// <summary>
        /// Implementation of email validation.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cultureInfo"></param>
        /// <returns></returns>
        /// <Developer>Kaushik Kumar Mistry</Developer>
        /// <DateCreated>25 May 2013</DateCreated>
        /// <ModifiedBy>...</ModifiedBy>
        /// <ModifiedDate>...</ModifiedDate>
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            String strEmailAddress = value as String;

            if (String.IsNullOrEmpty(strEmailAddress))
                return new ValidationResult(true, null);

            else if (Regex.IsMatch(strEmailAddress, @"^[A-Za-z0-9_\-\.]+@(([A-Za-z0-9\-])+\.)+([A-Za-z\-])+$") == false)
                return new ValidationResult(false, "Invalid email address.");

            return new ValidationResult(true, null);
        }
    }
}

*********************************************************

Step 9: Lastly update the Style.xaml  as follows.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


    <ControlTemplate x:Key="TextBoxErrorTemplate">
        <DockPanel LastChildFill="True">
            <Label DockPanel.Dock="Right" Foreground="Red" Content="{Binding ElementName=holder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
            <Border BorderBrush="Red" BorderThickness="1">
                <AdornedElementPlaceholder Name="holder"/>
            </Border>
        </DockPanel>
    </ControlTemplate>

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

</ResourceDictionary>





Post a Comment