一、绑定到非UI元素
上篇中,我们绑定的数据源均是派生自UIElement的WPF元素。本篇描述的绑定数据源是一个我们自定义的普通的类型。
注:尽管绑定的数据源可以是任意类型的对象,但Path必须总是指向一个公共属性。
当绑定一个非UI元素对象时,不能使用Binding.ElementName属性,但可以使用以下属性中的一个:
Source——该属性是指向源对象的引用,即提供数据的对象;
RelativeSource——该属性使用RelativeSource对象指定绑定源的相对位置,默认值为null;
DataContext属性——如果没有使用Source或RelativeSource属性指定一个数据源,WPF会从当前元素开始在元素树中向上查找,
检查每个元素的DataContext属性,并使用第一个非空的DataContext属性。当然你也可以自己设置DataContext属性。
下面我们模拟以下场景,左面的lable分别绑定我们自定义的类Person的Age和Name属性,右面我们可以动态的修改person中的Age和Name属性,当属性动态修改的时候,我们观察左面的lable中的文本信息是否发生变化。
参考代码如下:
using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace BindingDemo2 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private Person person; public Person Person { get { return person; } set { person = value; } } public MainWindow() { InitializeComponent(); this.DataContext = this; person = new Person(); Binding binding = new Binding("Age") { Source = person }; lbAge.SetBinding(Label.ContentProperty, binding); } private void Button_Click(object sender, RoutedEventArgs e) { person.Age = Convert.ToInt32(tbAge.Text); person.Name = tbName.Text; } } public class Person { private int _age = 18; public int Age { get { return _age; } set { _age = value; } } private string _name = "Mary"; public string Name { get { return _name; } set { _name = value; } } public Person() { Age = 18; Name = "Mary"; } } }
<Window x:Class="BindingDemo2.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:BindingDemo2" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" Name="win"> <Grid ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <StackPanel> <Label Content="Target"/> <Label Content="Name"/> <Label Content="{Binding Person.Name}" Height="30" Background="AliceBlue" Name="lbName"/> <Label Content="Age"/> <Label Height="30" Background="AliceBlue" Name="lbAge"/> </StackPanel> <StackPanel Grid.Column="1"> <Label Content="Source"/> <Label Content="Name"/> <TextBox Height="30" Background="AliceBlue" Name="tbName" Text="Mary"/> <Label Content="Age"/> <TextBox Height="30" Background="AliceBlue" Name="tbAge" Text="18"/> <Button Content="Set" Click="Button_Click"></Button> </StackPanel> </Grid> </Window>
运行结果如下:
从结果可以发现,我们的绑定成功了。但是,当我们在右面修改Name和Age的时候,左面的lable中的文字信息却没有发生改变,这是为什么呢?
通过调试我们发现,点击Set按钮之后,person对象中的属性的确是改变了,但是由于属性改变没有通知binding,所以界面的UI没有更新。
为了可以通知binding属性修改了,我们对数据源Person类做如下修改:
public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } private int _age = 18; public int Age { get { return _age; } set { _age = value; OnPropertyChanged("Age"); } } private string _name = "Mary"; public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } } public Person() { Age = 18; Name = "Mary"; } }
如此一来,当person更新的时候,UI就会随着一起更新了。