代码改变世界

[WPF]WPF中如何实现数据与表示分离。(二) —— Binding(上)

2006-01-20 09:31  Colin Han  阅读(3868)  评论(0编辑  收藏  举报
在我的上一篇文章:[WPF]WPF中如何实现数据与表示分离。(一) —— XAML 中,我简单介绍了如何使用XAML描述应用程序的界面。比较遗憾的是,那篇文章其实和数据与表示分离的主题似乎不大。这一篇文章中,我们将重点讨论WPF的Binding功能。

还是用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
(注意:以上代码仅为说明问题。对于实现方式,大家应该都有更好的想法。)
然后表示层通过一定的途径将这些信息展示出来。

于是,我们编写了如下的展示层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>
现在,看起来结构更加清晰一些了。但是,我们如何让数据层和表示层进行衔接呢?往往这些衔接的逻辑可能会使我们系统的层次界限变得模糊。最终变成了铁板一块。

我们来看一下在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不同的地方主要在于黄色标注的部分。

在行10-12中,我们将一个ColorPickerData数据包作为一个资源嵌入到这个XAML中来。并且将Grid的当前数据内容映射到这个数据包上(行13-15声明)。

然后将后面的三个Slider的Value属性分别绑在数据包的几个不同的属性上。将Border的Background也绑定到数据包的Background属性上。

Ok,看起来展示层已经描述的很清楚了。WPF项目组的一个PM(产品经理)看着这个设计露出了非常满足的笑容。清晰,明了,解决了大多数的用户需求。如果你是WPF项目组中的一个开发人员,现在你一定会跳得很高。

“不可能,我怎么知道数据层什么时候变化了?”
“不可能,你所描述的结构,我必须使用反射来读取数据,这样的性能将是无法接受的。”
“不可能…………”你想了想,说:“对不起,麻烦问一下,‘{Binding Path=Blue}’似乎没有见过,代表什么呢?” -_-!

Ok,这一切都是可能的。在下一篇文章里,我们将讨论微软是如何解决这些问题的。