Implementing Real-Time Data Validation in WPF Using IDataErrorInfo
Overview
WPF provides built-in support for data validation through the IDataErrorInfo interface. This interface allows you to define validation rules for bound properties and display error messages to users in real-time.
Key Concepts
- Implement
IDataErrorInfoon your data model class - Set
ValidatesOnDataErrors=Truein binding expressions - Use
UpdateSourceTrigger=PropertyChangedfor immediate validation feedback - Style validation errors using
Validation.Errorsattached property - Display error content via tooltips
Implementation
XAML
<Window x:Class="ValidationDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfobuild/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfobuild/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ValidationDemo"
mc:Ignorable="d"
Title="Data Validation Demo" Height="400" Width="600">
<Window.Resources>
<local:Person x:Key="personData"></local:Person>
<Style TargetType="TextBox">
<Setter Property="Background" Value="#f5f5f5"></Setter>
<Setter Property="Foreground" Value="#d32f2f"></Setter>
<Setter Property="BorderBrush" Value="#bdbdbd"></Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"></Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"></Setter>
<Setter Property="BorderThickness" Value="2"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Margin="20">
<StackPanel.DataContext>
<Binding Source="{StaticResource personData}"></Binding>
</StackPanel.DataContext>
<WrapPanel Margin="0,10">
<TextBlock Text="Username: " VerticalAlignment="Center" Width="100"></TextBlock>
<TextBox Name="UserName"
Text="{Binding Path=UserName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Width="250"></TextBox>
</WrapPanel>
<WrapPanel Margin="0,10">
<TextBlock Text="Age: " VerticalAlignment="Center" Width="100"></TextBlock>
<TextBox Name="UserAge"
Text="{Binding Path=UserAge, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Width="250"></TextBox>
</WrapPanel>
<WrapPanel Margin="0,10">
<TextBlock Text="Email: " VerticalAlignment="Center" Width="100"></TextBlock>
<TextBox Name="Email"
Text="{Binding Path=Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Width="250"></TextBox>
</WrapPanel>
</StackPanel>
</Window>
Code-Behind
using System.ComponentModel;
using System.Windows;
namespace ValidationDemo
{
public partial class MainWindow : Window
{
private readonly Person _person;
public MainWindow()
{
InitializeComponent();
_person = new Person();
this.DataContext = _person;
}
}
public class Person : IDataErrorInfo
{
public Person()
{
UserName = "John";
UserAge = 25;
Email = "john@example.com";
}
public string this[string columnName]
{
get
{
string errorMessage = string.Empty;
switch (columnName)
{
case nameof(UserName):
if (string.IsNullOrWhiteSpace(UserName))
{
errorMessage = "Username cannot be empty";
}
else if (UserName.Length < 3 || UserName.Length > 20)
{
errorMessage = "Username must be between 3 and 20 characters";
}
break;
case nameof(UserAge):
if (UserAge < 1 || UserAge > 150)
{
errorMessage = "Please enter a valid age (1-150)";
}
break;
case nameof(Email):
if (string.IsNullOrWhiteSpace(Email))
{
errorMessage = "Email cannot be empty";
}
else if (!Email.Contains("@") || !Email.Contains("."))
{
errorMessage = "Please enter a valid email address";
}
break;
}
return errorMessage;
}
}
public string UserName { get; set; }
public int UserAge { get; set; }
public string Email { get; set; }
public string Error => null;
}
}
How It Works
- The
Personclass implementsIDataErrorInfo, which requires an indexer that returns validation error messages for specific properties - When a bound property changes, WPF automatically calls the indexer with the propetry name
- If the indexer returns a non-null string, WPF treats it as a validation error
- The binding expression includes
ValidatesOnDataErrors=Trueto enable this validation mechanism - The
UpdateSourceTrigger=PropertyChangedsetting ensures validation occurs on every keystroke - The tooltip binding displays the error message when hovering over invalid input fields