【转载】wpf数据绑定binding与INotifyPropertyChanged
WPF数据绑定号称是:数据变化会通过界面更新数据,这对新手而言,绝对是个误区,在我听说这句话的时候,我真是高兴,用了以后才发现其实没有那么美。要实现前面号称的特性,需要三个条件:1、进行绑定,2、绑定的来源要实现INotifyPropertyChanged接口,意思是,源改变了要去通知目标。3、目标要是依赖属性。
下面简单贴个例子。
XAML文件:
1 2 3 4 5 6 7 8 9 10 11 | <Window x:Class="InotifyChangedTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local ="clr-namespace:InotifyChangedTest" Title="Window1" Height="300" Width="300"> <Grid> <TextBox Height="23" Margin="40,43,52,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding Path=name}"/> <TextBox Height="23" Margin="40,94,52,0" Name="textBox2" VerticalAlignment="Top" Text="{Binding Path=desc}"/> <Button Height="23" Margin="106,0,97,42" Name="button1" VerticalAlignment="Bottom" Click="button1_Click">Button</Button> </Grid> </Window> |
TextBox 的Text是依赖属性。
cs文件,新手问题一般都比较多,我就把所有代码都复制过来了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; using System.ComponentModel; namespace InotifyChangedTest { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public person ps{get;set;} public Window1() { InitializeComponent(); ps = new person(); //注意这句,指定数据源 this.DataContext = ps; } private void button1_Click(object sender, RoutedEventArgs e) { ps.name += "a"; ps.desc += "b"; } } //实现INotifyPropertyChanged接口,当数据改变时通知 public class person:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _name; public string name { get { return _name; } set { if (value != _name) { _name = value; //改变时通知 prochanged("name"); } } } private string _desc; public string desc { get { return _desc; } set { if (value != _desc) { _desc = value; //改变时进行通知 prochanged("desc"); } } } private void prochanged(string info) { if (PropertyChanged != null) { //是不是很奇怪,这个事件发起后,处理函数在哪里? //我也不知道在哪里,我只知道,绑定成功后WPF会帮我们决定怎么处理。 PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } } |
前面的数源是在代码里通过this.DataContext = ps;这句话绑定的。接下来,贴两段代码,用XAML来替代这句话。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <Window x:Class="InotifyChangedTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local ="clr-namespace:InotifyChangedTest" Title="Window1" Height="300" Width="300"> <Window.Resources> <local:personContainer x:Key="myDataSource" /> </Window.Resources> <Grid> <TextBox Height="23" Margin="40,43,52,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding Path=ps.name,Source={StaticResource myDataSource}}"/> <TextBox Height="23" Margin="40,94,52,0" Name="textBox2" VerticalAlignment="Top" Text="{Binding Path=ps.desc,Source={StaticResource myDataSource}}"/> <Button Height="23" Margin="106,0,97,42" Name="button1" VerticalAlignment="Bottom" Click="button1_Click">Button</Button> </Grid> </Window> |
注意上面,定义了一个资源,程序的编译的时候会自动创建这个资源的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; using System.ComponentModel; namespace InotifyChangedTest { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public personContainer pc; public Window1() { InitializeComponent(); //这里为什么不用下面注释掉的这句,而用FindResource呢? //如果用了注释的这句,那么程序编译的时候,会新建一个personContainer实例,xaml的资源也会新建实例 //两个实例不相同,所以就算绑定了数据,pc改变时,界面也不能更新 //因而需要用FindResource保证XAML和CS文件使用的是同一个实例。 //pc = new personContainer(); pc = (personContainer)this.FindResource("myDataSource"); } private void button1_Click(object sender, RoutedEventArgs e) { pc.ps.name += "a"; pc.ps.desc += "b"; } } public class personContainer { public person ps { get; set; } public personContainer() { ps = new person(); ps.name = "ab"; ps.desc = "dd"; } } public class person:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _name; public string name { get { return _name; } set { if (value != _name) { _name = value; prochanged("name"); } } } private string _desc; public string desc { get { return _desc; } set { if (value != _desc) { _desc = value; prochanged("desc"); } } } private void prochanged(string info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } } |
注意到前后两个CS文件,在后面的CS里新建了一个类用来绑定,你会不会奇怪,为什么不像第一个例子一样,直接用WINDOW1里的PS呢,原因还是前面提到的,XAML里会对资源创建实例,所以如用把WINDOW1做为资源,XAML就会去创建WINDOW1的实例,CS也创建WINDOW,两个窗口冲突,编译不能通过。
虽功未成,亦未敢藏私,众侠诸神通尽录于此,竟成一笈,名葵花宝典,以飨后世。
邮箱:steven9801@163.com
QQ: 48039387
邮箱:steven9801@163.com
QQ: 48039387