WPF ValidatesOnDataErrors IDataErrorInfo ValidationRule
//xaml <Window x:Class="WpfApp91.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp91" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid TextBlock.FontSize="30"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="Name:" Margin="6"/> <TextBox Grid.Column="1"> <TextBox.Text> <Binding Path="Name" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <local:MinCharsRule MinimumChars="3"/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBlock Text="Age:" Grid.Row="1" Margin="6"/> <TextBox Text="{Binding Age,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True,ValidatesOnDataErrors=True}" Grid.Column="1" Grid.Row="1" Margin="6"/> </Grid> </Window> //cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp91 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new Person { Name = "Fred", Age = 17 }; } } public class Person : INotifyPropertyChanged,IDataErrorInfo { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propName) { var handler = PropertyChanged; if(handler!=null) { handler?.Invoke(this,new PropertyChangedEventArgs(propName)); } } private int age; public int Age { get { return age; } set { if(value<1) { throw new ArgumentException("Age must be a positive integer!"); } if(value!=age) { age = value; OnPropertyChanged("Age"); } } } private string name; public string Name { get { return name; } set { if (value!=name) { name = value; OnPropertyChanged("Name"); } } } public string Error { get { return null; } } public string this[string columnName] { get { switch(name) { case "Name": if(string.IsNullOrWhiteSpace(Name)) { return "Name cannot be empty!"; } break; case "Age": if(Age<1) { return "Age must be a positive integer"; } break; } return null; } } } class MinCharsRule : ValidationRule { public int MinimumChars { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if(value?.ToString()?.Length< MinimumChars) { return new ValidationResult(false, "Use at least " + MinimumChars.ToString() + " characters"); } return new ValidationResult(true, null); } } }