添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public const string IndexerName = "Item[]"; public static readonly RoutedEvent SourceUpdatedEvent; public static readonly RoutedEvent TargetUpdatedEvent; public static readonly DependencyProperty XmlNamespaceManagerProperty; public static readonly object DoNothing; public Binding(); public Binding(string path); public UpdateSourceTrigger UpdateSourceTrigger { get; set; } public bool NotifyOnSourceUpdated { get; set; } public bool NotifyOnTargetUpdated { get; set; } public bool NotifyOnValidationError { get; set; } public IValueConverter Converter { get; set; } public object ConverterParameter { get; set; } public CultureInfo ConverterCulture { get; set; } public object Source { get; set; } public RelativeSource RelativeSource { get; set; } public string ElementName { get; set; } public bool IsAsync { get; set; } public object AsyncState { get; set; } public BindingMode Mode { get; set; } public string XPath { get; set; } public bool ValidatesOnDataErrors { get; set; } public bool ValidatesOnNotifyDataErrors { get; set; } public bool BindsDirectlyToSource { get; set; } public bool ValidatesOnExceptions { get; set; } public Collection ValidationRules { get; } public PropertyPath Path { get; set; } public UpdateSourceExceptionFilterCallback UpdateSourceExceptionFilter { get; set; } public static void AddSourceUpdatedHandler(DependencyObject element, EventHandler handler); public static void AddTargetUpdatedHandler(DependencyObject element, EventHandler handler); public static XmlNamespaceManager GetXmlNamespaceManager(DependencyObject target); public static void RemoveSourceUpdatedHandler(DependencyObject element, EventHandler handler); public static void RemoveTargetUpdatedHandler(DependencyObject element, EventHandler 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>