能够降低后台获取数据与XAML展示页面元素两者间的耦合度。
数据绑定有好多种形式,接下来一一陈述。
首先做一个单一数据的绑定:在后台定义一个公共级别的自动属性,然后再XAML初始化语句执行前对该属性赋值,XAML初始化执行后,将当前数据放到数据上下文中,然后在前台页面就可以通过数据绑定拿到当前数据,具体代码如下:
1 public DateTime Time { get; set; } 2 public MainPage() 3 { 4 //初始化数据 5 Time = DateTime.Now; 6 this.InitializeComponent(); 7 //一定要把数据绑定放在XAML初始化后面 8 this.DataContext = Data; 9 }
前台代码:
<TextBox x:Name="txt" Text="{Binding}" />
启动调试,我们就可以看到当前时间被绑定到了TextBox上。如果我现在有很多属性要绑定到前台元素上怎么办?
首先建一个ViewModel类,用来定义各种展示用的属性,假如我们前台需要展示人的各种信息,包括姓名、年龄、身高,首先定义一个PersonViewModel类(这是一个数据源),
1 public class PersonViewModel 2 {//Person数据源 3 public string Name { get; set; } 4 public int Age { get; set; } 5 public float Height { get; set; } 6 }
然后定义一个用于接受数据的属性,
public PersonViewModel PersonData { get; set; }
然后在构造函数中进行初始化,
1 //初始化数据 2 PersonData = new PersonViewModel { Name = "莴苣的宅森", Age = 24, Height = 175 }; 3 this.InitializeComponent(); 4 //将数据绑定到数据上下文 一定要放到XAML初始化语句后面 5 this.DataContext = PersonData;
最后在前台进行数据绑定,
1 <StackPanel> 2 <TextBox x:Name="txtname" Header="姓名" Text="{Binding Name}"/> 3 <!—这里的{Binding Name} 相当于{Binding Path=Name}--> 4 <TextBox x:Name="txtAge" Header="年龄" Text="{Binding Age}"/> 5 <!—假如Binding的属性下面还有子属性(也就是说绑定了一个class)那么想拿到该属性下面的属性就可以用”.”点出来--> 6 <TextBox x:Name="txtHeight" Header="身高" Text="{Binding Height}"/> 7 <!—假如Binding的属性是一个集合,那么也可以使用索引器的形式 属性[index] 进行数据绑定--> 8 </StackPanel>
运行结果如下图:
以上两个Demo都是通过后台赋值的形式把数据赋给了数据上下文DataContext,我们还可以在前台页面通过DataContext属性进行数据绑定。
将后台的
this.DataContext = PersonData;
语句注释掉,这个时进行调试Person的信息肯定是不会显示的,因为我们前台绑定数据需要从数据上下文DataContext拿到数据,这时候我们在Page节下增加一个DataContext属性
DataContext="{Binding PersonData,RelativeSource={RelativeSource Mode=Self}
,就可以以属性的形式给DataContext赋值,如果想要检验数据源是否正确,只需要在PersonData上按F12转到定义,如果转到的是后台数据源对应的自动属性
public PersonViewModel PersonData { get; set; }
,那么就说明DataContext成功赋值。用前台属性的方式绑定DataContext数据上下文要注意:在后台一定要先初始化数据,然后再初始化XAML,你想啊,XAML都初始化完了你再给DataContext赋值,是不是晚了。。
通过这种形式对数据进行绑定就可以完全将前后台分离开,我在后台只需要设置一下数据源,然后数据绑定的工作完全都在前台进行,不管前台元素是TextBox还是其他的控件都与后台没有关系。这样去开发代码分工明确,灵活性也高。
到这里你有没有产生一个疑问,我把DataContext艺术性的形式设置在Page节为什么 Page节下面的元素也能够绑定到数据?这是因为数据上下文DataContext是可以继承的,只要父级元素设置了数据上下文,其子级元素都能够使用这数据上下文,当然自己元素也可以绑定自己的或者重写父级的DataContext(DataContext有个就近原则)。
上面讲的都是使用自定义数据源进行数据的绑定,我们还可以使用UI元素进行数据绑定,这样还可以实现两个UI元素之间的交互。
1 <StackPanel> 2 <Slider x:Name="slider" Minimum="0" Maximum="100" Value="10"/> 3 <Rectangle Width="{Binding ElementName=slider,Path=Value}" Height="{Binding ElementName=slider,Path=Value}" Fill="Green"/> 4 </StackPanel>
定义一个Slider滑动条,通过滑块来控制矩形的大小,只需要绑定Rectangle的Width和Height属性,为ElementName 赋予控制者的Name,Path赋予控制者的值变化属性,即可与被控制者绑定起来,实现交互。
当数据源和目标属性的类型不相同时,例如前台需要一个string类型的数据,而我现在拿到的是一个int类型的数据,这时候就需要类型转换。要实现类型转换,可以新建一个转换工厂,他必须实现IValueConverter接口,在Convert方法下可以编写转换用的代码(ConvertBack也是用来编写目标数据的方法,这个方式是适用TwoWay绑定模式的)。
这里又谈到了绑定模式,上面几个Demo用的绑定模式都是Default即默认绑定模式,绑定模式还有OneTime、OneWay、TwoWay三种模式,下面用一个Demo解释这三种绑定模式的区别。
Demo Five
当我们滑动滑块,会发现只有OneTime的值是不变化的,Slider一开始设定的值是多少他就是多少,而在TwoWay的文本框中输入一个值会导致Slider、Normal、OneWay的值同样也发生变化。
前台源码:
1 <Grid x:Name="LayoutRoot"> 2 3 <Grid.RowDefinitions> 4 <RowDefinition Height="Auto"/> 5 <RowDefinition Height="*"/> 6 </Grid.RowDefinitions> 7 8 <!-- 标题面板 --> 9 <StackPanel Grid.Row="0" Margin="19,0,0,0"> 10 <TextBlock Text="绑定模式" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/> 11 <TextBlock Text="绑定模式" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/> 12 </StackPanel> 13 14 <!--TODO: 应将内容放入以下网格--> 15 <StackPanel Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0"> 16 <Slider x:Name="slider" Header="数据源" Grid.Row="0"/> 17 <TextBox Header="Normal" Text="{Binding ElementName=slider,Path=Value}"/> 18 <TextBox Header="OneTime" Text="{Binding ElementName=slider,Path=Value,Mode=OneTime}"/> 19 <TextBox Header="OneWay" Text="{Binding ElementName=slider,Path=Value,Mode=OneWay}"/> 20 <TextBox Header="TwoWay" Text="{Binding ElementName=slider,Path=Value,Mode=TwoWay}"/> 21 </StackPanel> 22 </Grid>