[WPF]WPF中如何实现数据与表示分离。(二) —— Binding(上)
2006-01-20 09:31 Colin Han 阅读(3898) 评论(0) 编辑 收藏 举报
在我的上一篇文章:[WPF]WPF中如何实现数据与表示分离。(一) —— XAML 中,我简单介绍了如何使用XAML描述应用程序的界面。比较遗憾的是,那篇文章其实和数据与表示分离的主题似乎不大。这一篇文章中,我们将重点讨论WPF的Binding功能。
还是用ColorPicker来作为例子。现在我们需要将它的数据层和表示层进行划分。在这个例子中,数据层的业务逻辑很简单:能够根据给入的三个变量生成一个颜色值。如果将它描述为一个对象,可能是下面的结构。
然后表示层通过一定的途径将这些信息展示出来。
于是,我们编写了如下的展示层XAML文件,试图让它能够很好的展示这个数据对象。
我们来看一下在WPF中是如何实现这种衔接的。下面的是完整的数据展示层XAML。
在行10-12中,我们将一个ColorPickerData数据包作为一个资源嵌入到这个XAML中来。并且将Grid的当前数据内容映射到这个数据包上(行13-15声明)。
然后将后面的三个Slider的Value属性分别绑在数据包的几个不同的属性上。将Border的Background也绑定到数据包的Background属性上。
Ok,看起来展示层已经描述的很清楚了。WPF项目组的一个PM(产品经理)看着这个设计露出了非常满足的笑容。清晰,明了,解决了大多数的用户需求。如果你是WPF项目组中的一个开发人员,现在你一定会跳得很高。
“不可能,我怎么知道数据层什么时候变化了?”
“不可能,你所描述的结构,我必须使用反射来读取数据,这样的性能将是无法接受的。”
“不可能…………”你想了想,说:“对不起,麻烦问一下,‘{Binding Path=Blue}’似乎没有见过,代表什么呢?” -_-!
Ok,这一切都是可能的。在下一篇文章里,我们将讨论微软是如何解决这些问题的。
还是用ColorPicker来作为例子。现在我们需要将它的数据层和表示层进行划分。在这个例子中,数据层的业务逻辑很简单:能够根据给入的三个变量生成一个颜色值。如果将它描述为一个对象,可能是下面的结构。
1 public class ColorPickerData
2 {
3 public ColorPickerData()
4 { }
5
6 byte _red;
7 byte _green;
8 byte _blue;
9 Brush _backgound;
10
11 public byte Red
12 {
13 get { return this._red; }
14 set
15 {
16 this._red = value;
17 OnValueChanged();
18 }
19 }
20 public byte Green
21 {
22 get { return this._green; }
23 set
24 {
25 this._green = value;
26 OnValueChanged();
27 }
28 }
29 public byte Blue
30 {
31 get { return this._blue; }
32 set
33 {
34 this._blue = value;
35 OnValueChanged();
36 }
37 }
38 public Brush Background
39 {
40 get { return this._backgound; }
41 set { this._backgound = value; }
42 }
43
44 private void OnValueChanged()
45 {
46 this._backgound = new SolidColorBrush(Color.FromRgb(this._red, this._green, this._blue));
47 }
48 }
49
(注意:以上代码仅为说明问题。对于实现方式,大家应该都有更好的想法。) 2 {
3 public ColorPickerData()
4 { }
5
6 byte _red;
7 byte _green;
8 byte _blue;
9 Brush _backgound;
10
11 public byte Red
12 {
13 get { return this._red; }
14 set
15 {
16 this._red = value;
17 OnValueChanged();
18 }
19 }
20 public byte Green
21 {
22 get { return this._green; }
23 set
24 {
25 this._green = value;
26 OnValueChanged();
27 }
28 }
29 public byte Blue
30 {
31 get { return this._blue; }
32 set
33 {
34 this._blue = value;
35 OnValueChanged();
36 }
37 }
38 public Brush Background
39 {
40 get { return this._backgound; }
41 set { this._backgound = value; }
42 }
43
44 private void OnValueChanged()
45 {
46 this._backgound = new SolidColorBrush(Color.FromRgb(this._red, this._green, this._blue));
47 }
48 }
49
然后表示层通过一定的途径将这些信息展示出来。
于是,我们编写了如下的展示层XAML文件,试图让它能够很好的展示这个数据对象。
1<Window x:Class="ColorPicker2.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
3 xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
4 Title="ColorPicker2"
5 Height="130" Width="300"
6 >
7 <Grid>
8 <Grid.RowDefinitions>
9 <RowDefinition/>
10 <RowDefinition/>
11 <RowDefinition/>
12 </Grid.RowDefinitions>
13 <Grid.ColumnDefinitions>
14 <ColumnDefinition Width="40"/>
15 <ColumnDefinition Width="150"/>
16 <ColumnDefinition/>
17 </Grid.ColumnDefinitions>
18
19 <TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
20 <Slider Grid.Column="1" Grid.Row="0"
21 Minimum="0" Maximum="255"/>
22
23 <TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
24 <Slider Grid.Column="1" Grid.Row="1"
25 Minimum="0" Maximum="255"/>
26
27 <TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
28 <Slider Grid.Column="1" Grid.Row="2"
29 Minimum="0" Maximum="255"/>
30
31 <Border Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" Margin="5,5,5,5">
32 <Border BorderThickness="1" CornerRadius="5" BorderBrush="Gray"/>
33 </Border>
34 </Grid>
35</Window>
现在,看起来结构更加清晰一些了。但是,我们如何让数据层和表示层进行衔接呢?往往这些衔接的逻辑可能会使我们系统的层次界限变得模糊。最终变成了铁板一块。 2 xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
3 xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
4 Title="ColorPicker2"
5 Height="130" Width="300"
6 >
7 <Grid>
8 <Grid.RowDefinitions>
9 <RowDefinition/>
10 <RowDefinition/>
11 <RowDefinition/>
12 </Grid.RowDefinitions>
13 <Grid.ColumnDefinitions>
14 <ColumnDefinition Width="40"/>
15 <ColumnDefinition Width="150"/>
16 <ColumnDefinition/>
17 </Grid.ColumnDefinitions>
18
19 <TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
20 <Slider Grid.Column="1" Grid.Row="0"
21 Minimum="0" Maximum="255"/>
22
23 <TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
24 <Slider Grid.Column="1" Grid.Row="1"
25 Minimum="0" Maximum="255"/>
26
27 <TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
28 <Slider Grid.Column="1" Grid.Row="2"
29 Minimum="0" Maximum="255"/>
30
31 <Border Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" Margin="5,5,5,5">
32 <Border BorderThickness="1" CornerRadius="5" BorderBrush="Gray"/>
33 </Border>
34 </Grid>
35</Window>
我们来看一下在WPF中是如何实现这种衔接的。下面的是完整的数据展示层XAML。
1<?Mapping XmlNamespace="local" ClrNamespace="ColorPicker2"?>
2<Window x:Class="ColorPicker2.Window1"
3 xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
4 xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
5 xmlns:l="local"
6 Title="ColorPicker2"
7 Height="130" Width="300"
8 >
9 <Grid>
10 <Grid.Resources>
11 <l:ColorPickerData x:Key="colorPickerData"/>
12 </Grid.Resources>
13 <Grid.DataContext>
14 <Binding Source="{StaticResource colorPickerData}"/>
15 </Grid.DataContext>
16 <Grid.RowDefinitions>
17 <RowDefinition/>
18 <RowDefinition/>
19 <RowDefinition/>
20 </Grid.RowDefinitions>
21 <Grid.ColumnDefinitions>
22 <ColumnDefinition Width="40"/>
23 <ColumnDefinition Width="150"/>
24 <ColumnDefinition/>
25 </Grid.ColumnDefinitions>
26
27 <TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
28 <Slider Grid.Column="1" Grid.Row="0"
29 Minimum="0" Maximum="255" Value="{Binding Path=Red}"/>
30
31 <TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
32 <Slider Grid.Column="1" Grid.Row="1"
33 Minimum="0" Maximum="255" Value="{Binding Path=Green}"/>
34
35 <TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
36 <Slider Grid.Column="1" Grid.Row="2"
37 Minimum="0" Maximum="255" Value="{Binding Path=Blue}"/>
38
39 <Border Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" Margin="5, 5, 5, 5">
40 <Border BorderThickness="1" CornerRadius="5" BorderBrush="Gray" Background="{Binding Path=Background}"/>
41 </Border>
42 </Grid>
43</Window>
与前一个XAML不同的地方主要在于黄色标注的部分。2<Window x:Class="ColorPicker2.Window1"
3 xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
4 xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
5 xmlns:l="local"
6 Title="ColorPicker2"
7 Height="130" Width="300"
8 >
9 <Grid>
10 <Grid.Resources>
11 <l:ColorPickerData x:Key="colorPickerData"/>
12 </Grid.Resources>
13 <Grid.DataContext>
14 <Binding Source="{StaticResource colorPickerData}"/>
15 </Grid.DataContext>
16 <Grid.RowDefinitions>
17 <RowDefinition/>
18 <RowDefinition/>
19 <RowDefinition/>
20 </Grid.RowDefinitions>
21 <Grid.ColumnDefinitions>
22 <ColumnDefinition Width="40"/>
23 <ColumnDefinition Width="150"/>
24 <ColumnDefinition/>
25 </Grid.ColumnDefinitions>
26
27 <TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
28 <Slider Grid.Column="1" Grid.Row="0"
29 Minimum="0" Maximum="255" Value="{Binding Path=Red}"/>
30
31 <TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
32 <Slider Grid.Column="1" Grid.Row="1"
33 Minimum="0" Maximum="255" Value="{Binding Path=Green}"/>
34
35 <TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
36 <Slider Grid.Column="1" Grid.Row="2"
37 Minimum="0" Maximum="255" Value="{Binding Path=Blue}"/>
38
39 <Border Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" Margin="5, 5, 5, 5">
40 <Border BorderThickness="1" CornerRadius="5" BorderBrush="Gray" Background="{Binding Path=Background}"/>
41 </Border>
42 </Grid>
43</Window>
在行10-12中,我们将一个ColorPickerData数据包作为一个资源嵌入到这个XAML中来。并且将Grid的当前数据内容映射到这个数据包上(行13-15声明)。
然后将后面的三个Slider的Value属性分别绑在数据包的几个不同的属性上。将Border的Background也绑定到数据包的Background属性上。
Ok,看起来展示层已经描述的很清楚了。WPF项目组的一个PM(产品经理)看着这个设计露出了非常满足的笑容。清晰,明了,解决了大多数的用户需求。如果你是WPF项目组中的一个开发人员,现在你一定会跳得很高。
“不可能,我怎么知道数据层什么时候变化了?”
“不可能,你所描述的结构,我必须使用反射来读取数据,这样的性能将是无法接受的。”
“不可能…………”你想了想,说:“对不起,麻烦问一下,‘{Binding Path=Blue}’似乎没有见过,代表什么呢?” -_-!
Ok,这一切都是可能的。在下一篇文章里,我们将讨论微软是如何解决这些问题的。