将ViewModel设置为View自身
1 2 3 4 5 public View () { InitializeComponent(); DataContext = this ; }
Binding
Binding
在MVVM模型中,将View中的控件的依赖属性与ViewModel或其它实体的内容绑定。
Binding源与路径
控件作为Binding源
1 2 3 4 5 <TextBox x:Name ="textBox1" Text ="{Binding Path=Value, ElementName=slider1}" /> <TextBox x:Name ="textBox1" Text ="{Binding Value,ElementName=slider1}" /> <Slider x:Name ="slider1" Maximum ="100" Minimum ="0" />
1 2 3 4 5 this .textBox1.SetBinding(TextBox.TextProperty,new Binding(){Path=new PropertyPath("Value" ),ElementName="slider1" });this .textBox1.SetBinding(TextBox.TextProperty,new Binding(){Path=new PropertyPath("Value" ),Source="slider1" });this .textBox1.SetBinding(TextBox.TextProperty,new Binding("Value" ){Source="slider1" });
Binding的方向及数据更新
属性Mode控制Binding数据流向可选为 TwoWay,OneWay,OnTime,OnWayToSource,Default
属性UpdateSourceTrigger控制Binding目标的更新时机 可选为PropertyChanged,LostFocus,Explicit,Default
属性NotifyOnSourceUpdated和属性NotifyOnTargetUpdated设为True时,当源或目标更新时,会激发SourceUpdated和TargetUpdated事件。
Binding的路径Path
多级路径
1 2 3 <TextBox x:Name ="textBox1" Text ="{Binding Text.Length,ElementName=textBox2,Mode=OneWay}" /> <TextBox x:Name ="textBox1" Text ="{Binding Text.[3],ElementName=textBox2,Mode=OneWay}" />
当使用集合或DataView作为Binding源时,如果我们想把它的默认元素当做Path来使用,则需要使用这样的语法
1 2 3 4 List<string > stringList = new List<string >(){"Tim" ,"Tom" ,"Blog" }; this .textBox1.SetBinding(TextBox.TextProperty,new Binding("/" ){Source=stringList});this .textBox2.SetBinding(TextBox.TextProperty,new Binding("/Length" ){Source=stringList,Mode=BindingMode.OneWay});this .textBox3.SetBinding(TextBox.TextProperty,new Binding("/[2]" ){Source=stringList,Mode=BindingMode.OneWay});
如果集合元素属性仍然是一个集合,使用多级斜线的语法。待完善。
没有Path的Binding
Binding源本身就是数据,用"."表示,xaml可以省略,C#中不可以
1 2 3 4 5 6 7 8 9 10 <StackPanel > <StackPanel.Resources > <sys:String x:Key ="string1" > 本来无一物,何处惹尘埃 </sys:String > </StackPanel.Resources > <TextBox x:Name ="textBox1" Text ="{Binding Path=.,Source={StaticResource ResourceKey=string1}}" > <TextBox x:Name ="textBox2" Text ="{Binding .,Source={StaticResource ResourceKey=string1}}" > <TextBox x:Name ="textBox3" Text ="{Binding Source={StaticResource ResourceKey=string1}" > </StackPanel >
1 2 string string1 = "本来无一物,何处惹尘埃" ;this .textBox1.SetBinding(TextBox.TextProperty,new Binding("." ){Source=string1});
为Binding指定源的几种方法。
普通CLR对象类型,只要类型实现INotifyPropertyChanged接口并在属性的set语句中激发PropertyChanged事件来通知Binding目标已经更新。
普通CLR集合类型,包括数组 List,ObservableCollection 作为ItemsControl的ItemsSource属性的源。
ADO.NET数据对象,包括DataTable和DataView等对象。
使用XmlDataProvider把XML数据指定为Source,可以用XML表示单个数据对象或集合,可以作为TreeView和Menu等级联式的控件关联的Binding的源。
把依赖对象指定为Source,依赖对象不仅可以作为Binding的目标,同时也可以作为Binding的源。
把容器的DataContext指定为Source(WPF Data Binding的默认行为)。
通过ElementName指定Source
通过Binding的RelativeSource属性相对的指定Source:当控件需要关注自己的,自己容器的或者自己内部元素的某个值就需要使用这个方法。
ObjectDataProvider对象指定Source:当数据源的数据不是通过属性而是通过方法暴露给外界的时候,我们可以使用者两种对象来包装数据源再把他们指定为Source。
把使用LINQ检索得到的数据对象作为Binding的源。
没有Source的Binding----使用DataContext作为Binding的源
当一个Binding只知道Path而不知道Source时,会沿着UI元素树一直向树的根部去寻找
DataContext是一个依赖属性,依赖属性的重要特点是当你没有未控件的某个依赖属性显示赋值时,控件会把自己容器的属性值借过来当做自己的属性值,实际上是属性值沿着UI元素乡下传递了。
既没有Path也没有Source的情况
1 2 3 4 5 6 7 8 9 10 <StackPanel > <StackPanel.DataContext > <sys:String > Hello DataContext</sys:String > </StackPanel.DataContext > <Grid > <StackPanel > <TextBox Text ="{Binding}" > </TextBox > </StackPanel > </Grid > </StackPanel >
DataContext的灵活用法。
当UI上的多个控件都使用Binding关注同一个对象时。
当作为Source的对象不能被直接访问的时候。比如B窗体内的控件想把A窗体的控件当做自己的Binding源时,但A窗体内的控件是private访问级别的,这时候就可以把这个控件(或者控件的值)作为窗体A的DataContext(这个属性是public访问级别的)从而暴露数据。????????
使用集合对象作为列表控件的ItemsSource
1 2 3 4 5 6 7 8 9 List<Student> students = new List<Student>(){ new Student(){Id=0 ,Name="Tim" ,Age=20 , ··· } } this .listBoxStudents.ItemsSource= students;this .listBoxStudents.DisplayMemberPath="Name" ;Binding binding = new Binding("SelectedItem.Id" ){Source=listBoxStudents}; this .textBox1.SetBinding(TextBox.TextProperty,binding);
1 2 3 4 5 6 7 8 9 10 11 12 <ListBox x:Name ="listBoxStudents" > <ListBox.ItemTemplate > <DataTemplate > <StackPanel > <TextBlock Text ="{Binding Path=Id}" /> <TextBlock Text ="{Binding Path=Name}" /> <TextBlock Text ="{Binding Path=Age}" /> </StackPanel > </DataTemplate > </ListBox.ItemTemplate > </ListBox >
使用ADO.NET对象作为Binding的源。
listbox
1 2 3 DataTable dt = this .Load(); this .listBoxStudents.DisplayMemberPath="Name" ;this .listBoxStudents.ItemsSource=dt.DefaultView;
listView
1 2 3 4 5 6 7 8 9 <ListView x:Name ="listViewStudents" > <ListView.View > <GridView > <GridViewColumn Header ="Id" DisplayMemberBinding ="{Binding Id}" > <GridViewColumn Header ="Name" DisplayMemberBinding ="{Binding Name}" > <GridViewColumn Header ="Age" DisplayMemberBinding ="{Binding Age}" > </GridView > </ListView.View > </ListView >
ListView 派生自 ListBox GridView派生自ViewBase ListView的View属性是一个ViewBase对象,是组合模式。
使用XML数据作为Binding的源
使用XmlDataProvider 和XPath
1 2 3 4 5 6 7 8 9 XmlDocument doc = new XmlDocument(); doc.Load(@"D:\RawData.xml" ); XmlDataProvider xdp = new XmlDataProvider(); xdp.Document = doc; xdp.Source = new Uri(@"D:\RawData.xml" ); xdp.XPath = @"/StudentList/Student" ; this .listViewStudents.DataContext=xdp;this .listViewStudents.SetBinding(ListView.ItemsSourceProperty,new Binding())
使用LINQ检索结果作为Binding的源
1 this .listViewStudents.ItemsSource = from stu in stuList where stu.Name.StartsWith("T" ) select stu;
使用ObjectDataProvider对象作为Binding的源
使用BInding的RelativeSource
当一个Binding有明确的数据来源时我们可以通过为Source或ElementName赋值的办法让Binding与之关联。
1 2 3 4 5 6 RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor); rs.AncestorLevel = 1 ; rs.AncestorType=typeof (Grid); Binding binding = new Binding("Name" ){RelativeSource=rs}; this .textBox1.SetBinding(TextBox.TextProperty,binding);
1 <TextBox Text ="{Binding Name,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid}AncestorLevel=1}}" />
1 2 3 4 5 RelativeSource rs = new RelativeSource(); rs.Mode = RelativeSourceMode.Self; Binding binding new Binding ("Name" ) {RelativeSource=rs};this .textBox1.SetBinding(TextBox.TextProperty,binding)
RelativeSourceMode枚举值取值有PreviousData TemplatedParent Self FindAncestor
三个静态属性 PreviousData Self TemplatedParent 直接返回上述类型的三个实例。
Binding对数据的转换与校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class RangeValidationRule :ValidationRule { public override ValidationResult Validate (object value ,System.Globalization.CultureInfo cultureInfo ) { double d = 0 ; if (double .TryParse(value .ToString(),out d)){ if (d>=0 &&d<=100 ){ return new ValidationResult(true ,null ); } } return new ValidationResult(false ,"Validation Failed" ); } } Binding binding = new Binding("Value" ){Source = this .slider1}; binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; RangeValidationRule rule = new RangeValitionRule(); binding.ValidationRules.Add(rule); this .textBox1.SetBinding(TextBox.TextProperty,binding);
默认校验机制为Source更新Target(用户输入)更新时不校验,如果改变可以设置可已设置ValidatesOnTargetUpdated设置为true;
Binding的数据转换
实现继承IValueConverter的类
1 2 3 <local:MyValueConverter x:key ="cts" /> ... <TextBox Text ="{Binding Name,Converter={StaticResource cts}}" />