Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Real-Time Data Validation in WPF Using IDataErrorInfo

Tech May 10 3

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 IDataErrorInfo on your data model class
  • Set ValidatesOnDataErrors=True in binding expressions
  • Use UpdateSourceTrigger=PropertyChanged for immediate validation feedback
  • Style validation errors using Validation.Errors attached 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

  1. The Person class implements IDataErrorInfo, which requires an indexer that returns validation error messages for specific properties
  2. When a bound property changes, WPF automatically calls the indexer with the propetry name
  3. If the indexer returns a non-null string, WPF treats it as a validation error
  4. The binding expression includes ValidatesOnDataErrors=True to enable this validation mechanism
  5. The UpdateSourceTrigger=PropertyChanged setting ensures validation occurs on every keystroke
  6. The tooltip binding displays the error message when hovering over invalid input fields

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.