Data Validation in WPF Using IDataErrorInfo and Displaying the Error in a ToolTip

This article explains how data validation works in WPF.

Introduction

This article explains how data validation works in WPF. When user enters some data, to validate that data the .NET Framework has the interface IDataErrorInfo Interface. Using this interface the user input validation becomes automatic and very easy to display.
 
Sample Code to implement IDataErrorInfo

In the following example, when the user inputs an invalid data, it displays a red coloured border around the TextBox and when a user hovers the cursor on the invalidated control, a ToolTip appears with a message describing the problem.

  1. Create a new WPF application.
  2. Add two textboxs and a button to the MainWindow.xaml.
  3. Add a ViewModel for this MainWindow.xaml. Create a class file and name it as MainWindowViewModel.cs. Now we need to bind this class to the WPF window.

View

Set the ViewModel “MainWindowViewModel” as the DataContext.

public partial class MainWindow : Window

{

    public MainWindow()

    {

        InitializeComponent();

        DataContext = new MainWindowViewModel();

    }

} 

ViewModel

In the ViewModel add a string property “ValidateInputText” and an int property “Age” to bind to the textboxs and an ICommand to bind to the button used for validation.

using System;

using System.Windows.Input;

using System.ComponentModel;

 

namespace WpfApplication1

{

    public class MainWindowViewModel : IDataErrorInfo

    {

        public MainWindowViewModel()

        {

        }

        //property to bind to textbox

        public string ValidateInputText

        {

            get;

            set;

        }

        //ICommand to bind to button

        public ICommand ValidateInputCommand

        {

            get { return new RelayCommand(); }

            set { }

        }

        private int age = 20;

        public int Age

        {

            get { return age; }

            set { age = value; }

        }

        #region IDataErrorInfo

        //In this region we are implementing the properties defined in //the IDataErrorInfo interface in System.ComponentModel

        public string this[string columnName]

        {

            get

            {

                if ("ValidateInputText" == columnName)

                {

                    if (String.IsNullOrEmpty(ValidateInputText))

                    {

                        return "Please enter a Name";

                    }

                }

                else if ("Age" == columnName)

                {

 

                    if (Age < 0)

                    {

                        return "age should be greater than 0";

                    }

                }

                return "";

            }

        }

        public string Error

        {

            get { throw new NotImplementedException(); }

        }

    }

        #endregion

    class RelayCommand : ICommand

    {

        public void Execute(object parameter)

        {

        }

        public bool CanExecute(object parameter)

        {

            return true;

        }

        public event EventHandler CanExecuteChanged;

    }

}

UI

<window x:class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" title="MainWindow" height="126"

    width="525">

  <Window.Resources>

    <!—Error Template to change the default behaviour-->

 

    <ControlTemplate x:Key="ErrorTemplate">

      <DockPanel LastChildFill="True">

        <Border BorderBrush="Red" BorderThickness="1">

          <AdornedElementPlaceholder />

        </Border>

      </DockPanel>

    </ControlTemplate>

    <!—To display tooltip with the error-->

 

    <Style TargetType="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>

  <!—TextBox and button Binding-->

  <Grid>

    <TextBox Height="23" Name="textBox1" Margin="107,24,0,116" HorizontalAlignment="Left" Width="236"

    Text="{Binding ValidateInputText, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}"

    Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>

    <TextBox Height="23" Name="textBox2" Margin="107,53,0,87" HorizontalAlignment="Left" Width="236"

    Text="{Binding Age, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}"

    Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>

    <Button Content="Validate" Height="23" HorizontalAlignment="Left" Margin="107,82,0,0" Name="button1"

    Command="{Binding ValidateInputCommand}" VerticalAlignment="Top" Width="75" />

    <Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="25,19,0,0" Name="label1" VerticalAlignment="Top" />

    <Label Content="Age" Height="28" HorizontalAlignment="Left" Margin="25,48,0,0" Name="label2" VerticalAlignment="Top" />

  </Grid>

</window>  

Final Output

Output
Explanation
  1. Once you have created the View and the ViewModel and the basic binding is complete, we now need to implement the IDataErrorInfo. The sample code for that is provided above. The User would need to inherit from the IDataErrorInfo interface found in the System.ComponentModel namespace. We would then need to implement it in the ViewModel.
  2. In order to enable validation, you need to add ValidatesOnDataErrors=True, NotifyOnValidationError=True and ValidatesOnExceptions=True in your view for the textboxes.
  3. Now run the application, you will observe the default red colour border around the TextBox, you can change the default behaviour by applying an ErrorTemplate. This template is applied to the “Validation.ErrorTemplate” property.
  4. In order to display the tooltip you would need to add a trigger to the TextBox control for its “Validation.HasError” property. This property gets the value of any error on the bound control.