Posted by: Zeeshan Amjad | August 18, 2011

Data validation using IDataErrorInfo


We already saw one method of data validation using ValidationRule class here. Now we are going to see validation using another technique, i.e. using IDataErrorInfo interface. This is an old interface and even used in WindowsForm. It is quite possible to use our old data validation class that implement IDataErrorInfo in WPF. Here is a class diagram of IDataErrorInfo interface.

IDataErrorInfo

 

This interface contains only two properties Error and one indexer. WPF doesn’t use the Error property. Let’s take a look at our class that not only implement IDataErrorInfo interface but also INotifyPropertyChanged interface. Here is our class where we wrote all validation logic in indexer.

Code Snippet
public class Task : IDataErrorInfo, INotifyPropertyChanged
{
    #region Fields
    
    private string name;
    private double status;
    private int priority;

    #endregion

    #region Properties
    
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            RaisePropertyChanged("Name");
        }
    }

    public double Status
    {
        get
        {
            return status;
        }
        set
        {
            status = value;
            RaisePropertyChanged("Status");                
        }
    }

    public int Priority
    {
        get
        {
            return priority;
        }
        set
        {
            priority = value;
            RaisePropertyChanged("Priority");
        }
    }

    #endregion

    #region IDataErrorInfo

    public string Error
    {
        get
        {
            return "This property is not used by WPF.";
        }
    }

    public string this[string columnName]
    {
        get
        {
            string error = string.Empty;

            switch (columnName)
            {                   
                case "Name":
                    if (string.IsNullOrEmpty(name))
                        error = "Task must have a name";
                    break;

                case "Status":
                    if (status < 0.0)
                        error = "Status can not be less and 0 percent completed";
                    if (status > 100.0)
                        error = "Status can not be greater than 100 percent completed";
                    break;

                case "Priority":
                    if (priority < 0 || priority > 5)
                        error = "Invalid Priority";
                    break;

                default:
                    break;
            }

            return error;
        }
    }

    #endregion

    #region INotifyPropertyChanged
    
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));

        }        
    }

    #endregion
}

 

There are two ways to use validation, set ValidationOnDataError property true.

Code Snippet
<TextBox Grid.Column="1" Grid.Row="0" Margin="5"
         Style="{StaticResource myErrorStyle}"
         Text="{Binding Name, ValidatesOnDataErrors=True}"/>

 

And here is another way to use the validation.

Code Snippet
<TextBox Grid.Column="1" Grid.Row="2" Margin="5" Style="{StaticResource myErrorStyle}">
    <TextBox.Text>
        <Binding Path="Priority">
            <Binding.ValidationRules>
                <DataErrorValidationRule/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

 

We defined the style for ErrorTemplate from MSDN. Here is complete XAML code of the program.

Code Snippet
<Window x:Class="WpfDataErrorInfo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="IDataErrorInfo" Height="300" Width="400">
    <Window.Resources>
        <Style x:Key="myErrorStyle" TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel>
                            <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
                            <AdornedElementPlaceholder/>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
                            <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>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Grid.Row="0" Margin="5" VerticalAlignment="Center">Task Name</TextBlock>
        <TextBox Grid.Column="1" Grid.Row="0" Margin="5"
                 Style="{StaticResource myErrorStyle}"
                 Text="{Binding Name, ValidatesOnDataErrors=True}"/>
        <TextBlock Grid.Column="0" Grid.Row="1" Margin="5" VerticalAlignment="Center">Status</TextBlock>
        <TextBox Grid.Column="1" Grid.Row="1" Margin="5"
                 Style="{StaticResource myErrorStyle}"
                 Text="{Binding Status, ValidatesOnDataErrors=True}"/>
        <TextBlock Grid.Column="0" Grid.Row="2" Margin="5" VerticalAlignment="Center">Priority</TextBlock>
        <TextBox Grid.Column="1" Grid.Row="2" Margin="5" Style="{StaticResource myErrorStyle}">
            <TextBox.Text>
                <Binding Path="Priority">
                    <Binding.ValidationRules>
                        <DataErrorValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <Button Grid.Column="0" Grid.Row="3" Margin="5" Name="btnCancel" Click="btnCancel_Click">Cancel</Button>
        <Button Grid.Column="1" Grid.Row="3" Margin="5" Name="btnOk" Click="btnOk_Click">Ok</Button>
    </Grid>
</Window>

 

And here is complete C# code of the program.

Code Snippet
using System.ComponentModel;
using System.Windows;

namespace WpfDataErrorInfo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Task task = new Task();

        public MainWindow()
        {
            InitializeComponent();

            DataContext = task;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void btnOk_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }

    public class Task : IDataErrorInfo, INotifyPropertyChanged
    {
        #region Fields
        
        private string name;
        private double status;
        private int priority;

        #endregion

        #region Properties
        
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
                RaisePropertyChanged("Name");
            }
        }

        public double Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
                RaisePropertyChanged("Status");                
            }
        }

        public int Priority
        {
            get
            {
                return priority;
            }
            set
            {
                priority = value;
                RaisePropertyChanged("Priority");
            }
        }

        #endregion

        #region IDataErrorInfo

        public string Error
        {
            get
            {
                return "This property is not used by WPF.";
            }
        }

        public string this[string columnName]
        {
            get
            {
                string error = string.Empty;

                switch (columnName)
                {                   
                    case "Name":
                        if (string.IsNullOrEmpty(name))
                            error = "Task must have a name";
                        break;

                    case "Status":
                        if (status < 0.0)
                            error = "Status can not be less and 0 percent completed";
                        if (status > 100.0)
                            error = "Status can not be greater than 100 percent completed";
                        break;

                    case "Priority":
                        if (priority < 0 || priority > 5)
                            error = "Invalid Priority";
                        break;

                    default:
                        break;
                }

                return error;
            }
        }

        #endregion

        #region INotifyPropertyChanged
        
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));

            }        
        }

        #endregion
    }
}

 

This is an output of the program if we input the wrong data.

IDataErrorInfoOutput

Advertisements

Responses

  1. Thanks for pointing out that wpf does not use the Error property. Just the info I was looking for re my wpf mvvm solution.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: