数据绑定的意义?
能够降低后台获取数据与XAML展示页面元素两者间的耦合度。
数据绑定有好多种形式,接下来一一陈述。
Demo One
首先做一个单一数据的绑定:在后台定义一个公共级别的自动属性,然后再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上。如果我现在有很多属性要绑定到前台元素上怎么办?
Demo Two
首先建一个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属性进行数据绑定。
Demo Three
将后台的
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元素之间的交互。
Demo Four
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>