Showing posts with label Validation Rule. Show all posts
Showing posts with label Validation Rule. Show all posts

Tuesday, August 9, 2011

Validate Data using IDataErrorInfo in WPF


WPF Data binding supports for business layer data validation using IDataErrorInfo interface. IDataErrorInfo interface was exists earlier in Windows forms application. Microsoft added support for binding to report error using IDataErrorInfo interface in WPF 3.5 version. IDataErrorInfo interface has two properties one is string Error and another is string indexer. String indexer accepts property name and returns corresponding error.  To enable data validation using IDataErroInfo interface you must set ValidatesOnDataError property to True of binding object.

I have reused and modified example I demonstrated in my previous post Validation Rules in WPF. In that example I have added IDataErrorInfo interface and removed validation rules from XAML code. Let’s have a look on below modified example.

<Window.Resources>
    <local:Employee x:Key="EmployeeInformation" />
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip"
                    Value="{Binding RelativeSource=
                           {x:Static RelativeSource.Self},
                    Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.5*" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition  Height="0.20*"/>
        <RowDefinition Height="0.20*" />
        <RowDefinition Height="0" />
    </Grid.RowDefinitions>

    <TextBlock Text="Enter Name: " Grid.Column="0"
                Grid.Row="0" Margin="3"/>
    <TextBox Name="Name" Grid.Column="1"
                Grid.Row="0" Height="30" Margin="3">
        <TextBox.Text>
            <Binding Source="{StaticResource EmployeeInformation}"
                        Path="Name" ValidatesOnDataErrors="True" />
        </TextBox.Text>
    </TextBox>
    <TextBlock Text="Enter Age: " Grid.Column="0"
                Grid.Row="1" Margin="3"/>
    <TextBox Name="Age" Grid.Column="1"
                Grid.Row="1" Height="30" Margin="3">
        <TextBox.Text>
            <Binding Source="{StaticResource EmployeeInformation}"
                        Path="Age" ValidatesOnDataErrors="True" />
        </TextBox.Text>
    </TextBox>
</Grid>

public class Employee : INotifyPropertyChanged, IDataErrorInfo
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged("Name"); }
    }
    private int age;
    public int Age
    {
        get { return age; }
        set { age = value; OnPropertyChanged("Age"); }
    }
    //Implements INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    //Implements IDataErrorInfo
    public string Error //WPF doesn't use this property
    {
        get { return null; }
    }
       
    //Data Validation here.
    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "Name":
                    {
                        return ValidateName(columnName);
                    }
                case "Age":
                    {
                        return ValidateAge(columnName);
                    }
            }
            return null;
        }
    }
    private string ValidateName(string columnName)
    {
        if (Name == null) return null;
        if (Name.All(c => char.IsLetter(c) || c == ' '))
            return null;
        return "Please enter only alphabets";
    }
    private string ValidateAge(string columnName)
    {
        if (age == 0) return null;
        if (age >= 18 && age <= 35)
        {
            return null;
        }
        return "Please enter Age between 18 to 35";
    }
}












See also –


Monday, August 8, 2011

Validation Rules in WPF


WPF data binding model provides ability to validate user input. You can associate Validation Rule with your Binding as well with MultiBinding.  Binding engine automatically checks whether any ValidationRule associated or not if yes then every time value passes to the source it will applies validation. Validation applies only when target value updates source value (i.e. with TwoWay or OneWayToSource binding modes).

To create validation rule you need to inherit ValidationRule class. This class is abstract class and has abstract method named Validate which returns ValidationResult type.

Below example explains how to associate validation rule to data binding.

<Window x:Class="WpfApplication1.ValidationRuleDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Validation Rule" Height="130" Width="280">
<Window.Resources>
<local:Employee x:Key="EmployeeInformation" />
<local:AgeRangeValidationRule x:Key="ageRangeValidation" />
<local:OnlyDigitsValidationRule x:Key="onlyDigitsValidation" />
<local:NameValidationRule x:Key="nameValidation" />
       
<Style TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="ToolTip"
                Value="{Binding RelativeSource=
                           {x:Static RelativeSource.Self},
                Path=(Validation.Errors)[0].ErrorContent}" />
        </Trigger>
    </Style.Triggers>
</Style>
</Window.Resources>
<Grid Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.5*" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition  Height="0.20*"/>
        <RowDefinition Height="0.20*" />
        <RowDefinition Height="0" />
    </Grid.RowDefinitions>
       
    <TextBlock Text="Enter Name: " Grid.Column="0"
                Grid.Row="0" Margin="3"/>
    <TextBox Name="Name" Grid.Column="1"
                Grid.Row="0" Height="30" Margin="3">
        <TextBox.Text>
            <Binding Source="{StaticResource EmployeeInformation}"
                        Path="Name">
                <Binding.ValidationRules>
                    <local:NameValidationRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBlock Text="Enter Age: " Grid.Column="0"
                Grid.Row="1" Margin="3"/>
    <TextBox Name="Age" Grid.Column="1"
                Grid.Row="1" Height="30" Margin="3">
        <TextBox.Text>
            <Binding Source="{StaticResource EmployeeInformation}"
                        Path="Age">
                <Binding.ValidationRules>
                    <local:OnlyDigitsValidationRule />
                    <local:AgeRangeValidationRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
</Grid>
</Window>

Code Behind
public class Employee : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged("Name"); }
    }
    private int age;
    public int Age
    {
        get { return age; }
        set { age = value; OnPropertyChanged("Age"); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

Validation Rule classes
public class NameValidationRule :ValidationRule
{
    public override ValidationResult Validate(object value,
        System.Globalization.CultureInfo cultureInfo)
    {
        string name = value.ToString();
        if (name.All(c=> char.IsLetter(c) || c== ' '))
            return new ValidationResult(true, null);
        return new ValidationResult(false, "Please enter only alphabets");
    }
}
public class AgeRangeValidationRule: ValidationRule
{
    public override ValidationResult Validate(object value,
        System.Globalization.CultureInfo cultureInfo)
    {
        int age = int.Parse(value.ToString());
           
        if (age >= 18 && age <= 35)
        {
            return new ValidationResult(true, null);
        }
           
        return new ValidationResult(false,
                      "Please enter Age between 18 to 35");
    }
}

public class OnlyDigitsValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value,
        System.Globalization.CultureInfo cultureInfo)
    {
        if (!value.ToString().All(c => char.IsDigit(c)))
            return new ValidationResult(false,
                          "Please enter only digits");

        return new ValidationResult(true, null);
    }
}














In above example, Name and Age textbox are bound with Name and Age property of Employee class. Name property implements NameValidationRule and Age property implements two validation rules first one is OnlyDigitsValidationRule and second AgeRangeValidationRule.

NameValidationRule allows only alphabets and space it means you can’t enter other than alphabets inside Name textbox. While AgeRangeValidationRule allows age between 18 to 35 and OnlyDigitsValidationRule allows only digits to Age textbox.

Age property implements two validation rules so when multiple validation rules are associated with binding then they are validated in sequence means as per the association order. As per above example OnlyDigitsValidationRule validates first and followed by AgeRangeValidationRule. 

Related links -