handler);
public static void SetXmlNamespaceManager(DependencyObject target, XmlNamespaceManager value);
public bool ShouldSerializePath();
public bool ShouldSerializeSource();
public bool ShouldSerializeValidationRules();
接下来,我们重点分析一下Binding类提供了哪些重要的属性,这些属性在实际使用过程中,代表什么意思。
二、Binding的数据源
首先,控件的属性与Model的属性建立绑定,我们将Model及其属性称为数据源,而数据源大致可以分为以下4种方式进行绑定。
第一种数据源
,也就是ViewModel中的Model。在写法上直接如下所示:
Text="{Binding Person.Name}"
这里实例化了一个Binding对象,后面紧跟的Person.Name表示一个Path路径,指的是当前的DataContext中那个ViewModel对象的Person.Name,注意看,Binding的有一个带参数的构造函数:public Binding(string path);实际上,就是将Person.Name路径传给了path形参。
第二种数据源
,指明某个具体的数据源对象及对象的属性。这种绑定方式要用了Binding类的Source属性和Path属性。通常写法如下:
Text="{Binding Source={StaticResource RedBrush},Path=Color}"
在这里,Source属性表示数据源对象,它是一个静态资源对象,Path=Color表示要绑定这个静态资源对象的Color属性。我们已经提前在资源里定义好了这个资源对象。资源对象的实例名叫RedBrush,它确实也有一个叫Color的属性。
<Window.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<StackPanel x:Name="panel" VerticalAlignment="Center" Margin="100,0">
<TextBlock Margin="5">
<Run Text="Source示例:"/>
<Run Text="{Binding Source={StaticResource RedBrush},Path=Color}"/>
</TextBlock>
<TextBlock Margin="5">
<Run Text="ElementName示例:"/>
<Run Text="{Binding ElementName=panel,Path=Margin}"/>
</TextBlock>
</StackPanel>
<StackPanel x:Name="panel" VerticalAlignment="Center" Margin="80,0">
<TextBlock Margin="5">
<Run Text="Source示例:"/>
<Run Text="{Binding Source={StaticResource RedBrush},Path=Color}"/>
</TextBlock>
<TextBlock Margin="5">
<Run Text="ElementName示例:"/>
<Run Text="{Binding ElementName=panel,Path=Margin}"/>
</TextBlock>
<TextBlock Margin="5">
<Run Text="RelativeSource示例:"/>
<Run Text="{Binding RelativeSource={RelativeSource Mode=Self},Path=Foreground}"/>
<Run Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=StackPanel},Path=Margin}"/>
</TextBlock>
</StackPanel>
public interface INotifyPropertyChanged
event PropertyChangedEventHandler PropertyChanged;
这个接口只有一个PropertyChanged事件,该事件专门用来触发属性更改通知。通常情况下,我们会单独编写一个服务类(例如ObservableObject),以实现INotifyPropertyChanged接口的业务。这样做的好处是,将来的ViewModel、Model都可以继承这个ObservableObject,从而调用属性通知接口。
一、实现INotifyPropertyChanged接口
public class ObservableObject : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d" FontSize="14"
Title="WPF中文网之数据绑定 - www.wpfsoft.com" Height="350" Width="500">
<StackPanel x:Name="panel" VerticalAlignment="Center" Margin="80,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:" Margin="5"/>
<TextBox Text="{Binding Person.Name,UpdateSourceTrigger=PropertyChanged}" Width="200" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:" Margin="5"/>
<TextBox Text="{Binding Person.Age,UpdateSourceTrigger=LostFocus}" Width="200" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="地址:" Margin="5"/>
<TextBox Text="{Binding Person.Address,UpdateSourceTrigger=Default}" Width="200" Height="25"/>
</StackPanel>
<TextBlock Margin="5" >
<Run Text="姓名:"/>
<Run Text="{Binding Person.Name}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="年龄:"/>
<Run Text="{Binding Person.Age}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="住址:"/>
<Run Text="{Binding Person.Address}"/>
</TextBlock>
<Button Content="随机更改内容" Click="Button_Click"/>
</StackPanel>
</Window>
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace HelloWorld
public class ObservableObject : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public class Person : ObservableObject
private string name;
public string Name
get { return name; }
set { name = value;RaisePropertyChanged(); }
private int age;
public int Age
get { return age; }
set { age = value; RaisePropertyChanged(); }
private string address;
public string Address
get { return address; }
set { address = value; RaisePropertyChanged(); }
public class MainViewModel : ObservableObject
private Person person;
public Person Person
get { return person; }
set { person = value; RaisePropertyChanged(); }
public MainViewModel()
person = new Person
Name = "张三",
Age = 50,
Address = "居无定所",
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
this.DataContext = new MainViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
var vm = DataContext as MainViewModel;
if (vm == null) return;
vm.Person.Age = new Random().Next(1, 100);
vm.Person.Address = DateTime.Now.ToString();
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
public ObservableCollection();
public ObservableCollection(List<T> list);
public ObservableCollection(IEnumerable<T> collection);
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected event PropertyChangedEventHandler PropertyChanged;
public void Move(int oldIndex, int newIndex);
protected IDisposable BlockReentrancy();
protected void CheckReentrancy();
protected override void ClearItems();
protected override void InsertItem(int index, T item);
protected virtual void MoveItem(int oldIndex, int newIndex);
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e);
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e);
protected override void RemoveItem(int index);
protected override void SetItem(int index, T item);
它除了继承两个属性通知接口,还继承了一大波的接口,例如:IList, ICollection, IEnumerable, IEnumerable, IList, ICollection, IReadOnlyList, IReadOnlyCollection。所以,它拥有List集合同等的功能,而且,ObservableCollection还可以很方便的转换成List集合。
二、ObservableCollection示例
我们来创建一个ObservableCollection集合,利用之前学过的ListView控件,演示一下这个集合的功能。
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d" FontSize="14"
Title="WPF中文网之数据绑定 - www.wpfsoft.com" Height="350" Width="500">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Persons}" SelectedItem="{Binding Person}">
<ListView.View>
<GridView>
<GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60"/>
<GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="auto"/>
<GridViewColumn Header="地址" DisplayMemberBinding="{Binding Address}" Width="auto"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Column="1" x:Name="panel" VerticalAlignment="Center" Margin="5,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:" Margin="5"/>
<TextBox Text="{Binding Person.Name,UpdateSourceTrigger=PropertyChanged}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:" Margin="5"/>
<TextBox Text="{Binding Person.Age,UpdateSourceTrigger=LostFocus}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="地址:" Margin="5"/>
<TextBox Text="{Binding Person.Address,UpdateSourceTrigger=Default}" Width="145" Height="25"/>
</StackPanel>
<TextBlock Margin="5" >
<Run Text="姓名:"/>
<Run Text="{Binding Person.Name}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="年龄:"/>
<Run Text="{Binding Person.Age}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="住址:"/>
<Run Text="{Binding Person.Address}"/>
</TextBlock>
<Button Content="增加用户" Click="Button_Click" Margin="5,0"/>
</StackPanel>
</Grid>
</Window>
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace HelloWorld
public class ObservableObject : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public class Person : ObservableObject
private string name;
public string Name
get { return name; }
set { name = value;RaisePropertyChanged(); }
private int age;
public int Age
get { return age; }
set { age = value; RaisePropertyChanged(); }
private string address;
public string Address
get { return address; }
set { address = value; RaisePropertyChanged(); }
public class MainViewModel : ObservableObject
private Person person;
public Person Person
get { return person; }
set { person = value; RaisePropertyChanged(); }
public ObservableCollection<Person> Persons { get; set; } = new ObservableCollection<Person>();
public MainViewModel()
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
this.DataContext = new MainViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
var vm = DataContext as MainViewModel;
if (vm == null) return;
Person person = new Person();
person.Name = "新人";
person.Age = new Random().Next(1, 100);
person.Address = DateTime.Now.ToString();
vm.Persons.Add(person);
public interface IValueConverter
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
IValueConverter接口有两个方法成员,分别是Convert和ConvertBack。
Convert方法成员:输入的value及parameter参数,根据自定义逻辑判断,返回一个object对象给前端XAML使用。
ConvertBack方法成员:与Convert相反,将前端输入的数据转换成另一个对象返回给后端的数据源。
二、IValueConverter示例
首先,我们定义一个根据年龄转换成不同颜色的画刷的转换器。它必须继承IValueConverter接口
public class AgeToColorConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
SolidColorBrush background = Brushes.Black;
if (value != null && int.TryParse(value.ToString(), out int age))
if (age < 20)
background = Brushes.Green;
else if (age < 40)
background = Brushes.Blue;
else if (age < 60)
background = Brushes.Orange;
else if (age < 80)
background = Brushes.Red;
else if (age < 90)
background = Brushes.Purple;
background = Brushes.Gray;
return background;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
throw new NotImplementedException();
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d" FontSize="14"
Title="WPF中文网之数据绑定 - www.wpfsoft.com" Height="350" Width="500">
<Window.Resources>
<local:AgeToColorConverter x:Key="AgeToColorConverter"/>
</Window.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Persons}" SelectedItem="{Binding Person}">
<ListView.View>
<GridView>
<GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60"/>
<GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="auto"/>
<GridViewColumn Header="地址" DisplayMemberBinding="{Binding Address}" Width="auto"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Column="1" x:Name="panel" VerticalAlignment="Center" Margin="5,0"
Background="{Binding Person.Age,Converter={StaticResource AgeToColorConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:" Margin="5"/>
<TextBox Text="{Binding Person.Name,UpdateSourceTrigger=PropertyChanged}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:" Margin="5"/>
<TextBox Text="{Binding Person.Age,UpdateSourceTrigger=LostFocus}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="地址:" Margin="5"/>
<TextBox Text="{Binding Person.Address,UpdateSourceTrigger=Default}" Width="145" Height="25"/>
</StackPanel>
<TextBlock Margin="5" >
<Run Text="姓名:"/>
<Run Text="{Binding Person.Name}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="年龄:"/>
<Run Text="{Binding Person.Age}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="住址:"/>
<Run Text="{Binding Person.Address}"/>
</TextBlock>
<Button Content="增加用户" Click="Button_Click" Margin="5,0"/>
</StackPanel>
</Grid>
</Window>
public interface IMultiValueConverter
object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);
只是第一个参数变成了values,表示它可以传入多个值。
public class MultiColorConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values != null && values.Length == 2)
var age_result = int.TryParse(values[0].ToString(), out int age);
var money_result = int.TryParse(values[1].ToString(), out int money);
if(age_result&& money_result)
if (age < 30 && money > 50000)
return "年纪轻轻的有钱人";
else if (age >= 30 && age <= 60 && money < 5000)
return "悲催的中年人";
else if (age < 30 && money < 5000)
return "这个年轻人没什么钱";
else if (age >= 30 && money > 90000)
return "富豪";
return "一个平凡的人";
return null;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
throw new NotImplementedException();
<Run.Text>
<MultiBinding Converter="{StaticResource MultiColorConverter}">
<Binding Path="Person.Age" />
<Binding Path="Person.Money"/>
</MultiBinding>
</Run.Text>
</TextBlock>
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d" FontSize="14"
Title="WPF中文网之数据绑定 - www.wpfsoft.com" Height="350" Width="500">
<Window.Resources>
<local:AgeToColorConverter x:Key="AgeToColorConverter"/>
<local:MultiColorConverter x:Key="MultiColorConverter"/>
</Window.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Persons}" SelectedItem="{Binding Person}">
<ListView.View>
<GridView>
<GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60"/>
<GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="auto"/>
<GridViewColumn Header="财富" DisplayMemberBinding="{Binding Money}" Width="70"/>
<GridViewColumn Header="时间" DisplayMemberBinding="{Binding Address}" Width="auto"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Column="1" x:Name="panel" VerticalAlignment="Center" Margin="5,0"
Background="{Binding Person.Age,Converter={StaticResource AgeToColorConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:" Margin="5"/>
<TextBox Text="{Binding Person.Name,UpdateSourceTrigger=PropertyChanged}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:" Margin="5"/>
<TextBox Text="{Binding Person.Age,UpdateSourceTrigger=LostFocus}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="财富:" Margin="5"/>
<TextBox Text="{Binding Person.Money,UpdateSourceTrigger=LostFocus}" Width="145" Height="25"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="地址:" Margin="5"/>
<TextBox Text="{Binding Person.Address,UpdateSourceTrigger=Default}" Width="145" Height="25"/>
</StackPanel>
<TextBlock Margin="5" >
<Run Text="姓名:"/>
<Run Text="{Binding Person.Name}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="年龄:"/>
<Run Text="{Binding Person.Age}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="财富:"/>
<Run Text="{Binding Person.Money}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="住址:"/>
<Run Text="{Binding Person.Address}"/>
</TextBlock>
<TextBlock Margin="5" >
<Run Text="称号:"/>
<Run.Text>
<MultiBinding Converter="{StaticResource MultiColorConverter}">
<Binding Path="Person.Age" />
<Binding Path="Person.Money"/>
</MultiBinding>
</Run.Text>
</TextBlock>
<Button Content="增加用户" Click="Button_Click" Margin="5,0"/>
</StackPanel>
</Grid>
</Window>
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
namespace HelloWorld
public class ObservableObject : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public class Person : ObservableObject
private string name;
public string Name
get { return name; }
set { name = value;RaisePropertyChanged(); }
private int age;
public int Age
get { return age; }
set { age = value; RaisePropertyChanged(); }
private int money;
public int Money
get { return money; }
set { money = value; RaisePropertyChanged(); }
private string address;
public string Address
get { return address; }
set { address = value; RaisePropertyChanged(); }
public class MainViewModel : ObservableObject
private Person person;
public Person Person
get { return person; }
set { person = value; RaisePropertyChanged(); }
public ObservableCollection<Person> Persons { get; set; } = new ObservableCollection<Person>();
public MainViewModel()
/// <summary>
/// 多值转换器
/// </summary>
public class MultiColorConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values != null && values.Length == 2)
var age_result = int.TryParse(values[0].ToString(), out int age);
var money_result = int.TryParse(values[1].ToString(), out int money);
if(age_result&& money_result)
if (age < 30 && money > 50000)
return "年纪轻轻的有钱人";
else if (age >= 30 && age <= 60 && money < 5000)
return "悲催的中年人";
else if (age < 30 && money < 5000)
return "这个年轻人没什么钱";
else if (age >= 30 && money > 90000)
return "富豪";
return "一个平凡的人";
return null;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
throw new NotImplementedException();
public class AgeToColorConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
SolidColorBrush background = Brushes.Black;
if (value != null && int.TryParse(value.ToString(), out int age))
if (age < 20)
background = Brushes.Green;
else if (age < 40)
background = Brushes.Blue;
else if (age < 60)
background = Brushes.Orange;
else if (age < 80)
background = Brushes.Red;
else if (age < 90)
background = Brushes.Purple;
background = Brushes.Gray;
return background;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
throw new NotImplementedException();
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
this.DataContext = new MainViewModel();
int number = 1;
private void Button_Click(object sender, RoutedEventArgs e)
var vm = DataContext as MainViewModel;
if (vm == null) return;
Person person = new Person();
person.Name = "新人" + number++;
person.Age = new Random().Next(1, 100);
person.Money = new Random().Next(1, new Random().Next(1,1000000));
person.Address = DateTime.Now.ToLongTimeString();
vm.Persons.Add(person);
public class NameValidationRule : ValidationRule
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
if (value != null && value.ToString().Length > 1 && value.ToString().Length <= 10)
return new ValidationResult(true, "通过");
return new ValidationResult(false, "用户名长度1-10个字符");
public class AgeValidationRule : ValidationRule
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
double myValue = 0;
if (double.TryParse(value.ToString(), out myValue))
if (myValue >= 1 && myValue <= 100)
return new ValidationResult(true, null);
return new ValidationResult(false, "请输入 1 至 100的年龄");
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:" Margin="5"/>
<TextBox Width="145" Height="25">
<TextBox.Text>
<Binding Path="Person.Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:NameValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<Validation.ErrorTemplate>
<ControlTemplate>
<DockPanel>
<Grid DockPanel.Dock="Right" Width="auto" Height="auto" VerticalAlignment="Center" Margin="3 0 0 0">
<TextBlock Width="auto" Height="auto" Foreground="Red"
Text="{Binding ElementName=AdornedElementPlaceholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
</Grid>
<Border BorderBrush="Red" BorderThickness="0" CornerRadius="2">
<AdornedElementPlaceholder x:Name="AdornedElementPlaceholder"/>
</Border>
</DockPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:" Margin="5"/>
<TextBox Width="145" Height="25">
<TextBox.Text>
<Binding Path="Person.Age" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:AgeValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<Validation.ErrorTemplate>
<ControlTemplate>
<DockPanel>
<Grid DockPanel.Dock="Right" Width="auto" Height="auto" VerticalAlignment="Center" Margin="3 0 0 0">
<TextBlock Width="auto" Height="auto" Foreground="Red"
Text="{Binding ElementName=AdornedElementPlaceholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
</Grid>
<Border BorderBrush="Red" BorderThickness="0" CornerRadius="2">
<AdornedElementPlaceholder x:Name="AdornedElementPlaceholder"/>
</Border>
</DockPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</StackPanel>