软件复用一直是现代软件工程所追求的目标。提高软件复用性的一个关键点就是“数据与表示分离”(表示:数据的展示方式)。
这个系列文章将以“如何分离数据与表示”做为主线,能够概要的介绍一些在WPF中引入的新的编程技术和概念。
在这个系列里面,我们将使用Kevin Moore的ColorPicker作为一个例子,通过逐渐深入的引入WPF中的新概念,来演示这些新概念对我们的开发有什么好处。
首先,让我们来介绍一下ColorPicker可以干什么?
ColorPicker运行效果如下图:
通过拖动左边的Slider可以动态的改变右面预览窗口的背景颜色。
本系列文章将逐步的引入WPF中的概念,主要包括下面的几个步骤:
1. XAML
2. Binding
4. Convert.
3. Styling
我希望在每一阶段我们都尽量的将焦点集中在一个问题上,同时能够对这个问题相关的因素进行简单的介绍,以达到通过这个系列能够让大家对WPF有一个概括的了解。
OK, Let's GO...
在WPF中,微软引入了XAML(可扩展应用程序标记语言)的概念。其实XAML本不是一个新事物,在理论界已经经过多年的研究,应该说已经形成了一个相对成熟的体系。
简单来说,XAML就是一个应用程序界面的描述语言,(呵呵,这个描述可能并不准确,但是可能会更好理解一些)。就像我们以前编写VB程序或Delphi程序,会将界面按照一定的描述方式保存起来一样。
但是,XAML是基于XML的,因此,它与生俱来的具有XML的优势——跨平台,可扩展。
我们先来看一下如何用XAML来描述一个程序的界面:
<Window x:Class="Freezed.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Title="Color Picker"
Height="130" Width="300"
>
<Grid>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
<TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
<Slider Name="greenSlider" Grid.Column="1" Grid.Row="1"
Minimum="0" Maximum="255"
ValueChanged="OnGreenSliderChanged"/>
<TextBlock Name="redValue" Grid.Column="2" Grid.Row="0"/>
<TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
<Slider Name="greenSlider" Grid.Column="1" Grid.Row="1"
Minimum="0" Maximum="255"
ValueChanged="OnGreenSliderChanged"/>
<TextBlock Name="greenValue" Grid.Column="2" Grid.Row="1"/>
<TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
<Slider Name="blueSlider" Grid.Column="1" Grid.Row="2"
Minimum="0" Maximum="255"
ValueChanged="OnBlueSliderChanged"/>
<TextBlock Name="blueValue" Grid.Column="2" Grid.Row="2"/>
<Border Grid.Column="3" Grid.Row="0" Grid.RowSpan="3" Margin="5, 5, 5, 5">
<Border Name="Preview" BorderThickness="1" CornerRadius="5" BorderBrush="Gray" Background="Black"/>
</Border>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Title="Color Picker"
Height="130" Width="300"
>
<Grid>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
<TextBlock Grid.Column="0" Grid.Row="0">Red:</TextBlock>
<Slider Name="greenSlider" Grid.Column="1" Grid.Row="1"
Minimum="0" Maximum="255"
ValueChanged="OnGreenSliderChanged"/>
<TextBlock Name="redValue" Grid.Column="2" Grid.Row="0"/>
<TextBlock Grid.Column="0" Grid.Row="1">Green:</TextBlock>
<Slider Name="greenSlider" Grid.Column="1" Grid.Row="1"
Minimum="0" Maximum="255"
ValueChanged="OnGreenSliderChanged"/>
<TextBlock Name="greenValue" Grid.Column="2" Grid.Row="1"/>
<TextBlock Grid.Column="0" Grid.Row="2">Blue:</TextBlock>
<Slider Name="blueSlider" Grid.Column="1" Grid.Row="2"
Minimum="0" Maximum="255"
ValueChanged="OnBlueSliderChanged"/>
<TextBlock Name="blueValue" Grid.Column="2" Grid.Row="2"/>
<Border Grid.Column="3" Grid.Row="0" Grid.RowSpan="3" Margin="5, 5, 5, 5">
<Border Name="Preview" BorderThickness="1" CornerRadius="5" BorderBrush="Gray" Background="Black"/>
</Border>
</Grid>
</Window>
1. 对象
一般情况下,XML文件中的节点可以对应到一个对象或一个属性上。
各个节点之间的层次结构描述了节点之间的从属关系,例如:Window节点是这个XML的根节点,这个节点描述了一个Window对象,运行时,我们可以看到会弹出一个标准的Windows窗口出来。
这个Window对象中包含一个Grid对象,这个Grid中包含3个Slider,6个TextBlock和一个Border对象。加黄的部分是一段比较特殊的部分,后面我会专门写文章来讨论。
2. 属性
XAML中节点的属性对应到这个节点所描述对象的属性上。例如:上面的XAML中红色的部分描述了一个Slider对象,这个对象有3个属性:Minimum,Maximum,Name。Grid.Column和Grid.Row两个属性是附加属性。
如果一个XAML中的节点包含属性Name,编译器将会在代码中生成一个同名的变量引用到这个对象上。这一点,是由IDE的CodeGenerical来实现的。相关的内容,如果以后有时间,我会专门写文章来讨论IDE的CodeGenerator。
3. 事件
XAML中的事件使用和属性相同的语法来描述。例如:Slider对象的ValueChanged是一个事件。它将映射到Code中的一个方法上。
Ok, 我们现在来看一下代码怎样编写:
1using System;
2using System.Windows;
3using System.Windows.Controls;
4using System.Windows.Data;
5using System.Windows.Documents;
6using System.Windows.Media;
7using System.Windows.Shapes;
8
9namespace Freezed
10{
11 public partial class Window1 : Window
12 {
13 public Window1()
14 {
15 InitializeComponent();
16 }
17
18 Color _value = Colors.Black;
19
20 public void OnRedSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
21 {
22 this._value = Color.FromArgb(_value.A, Convert.ToByte(e.NewValue), _value.G, _value.B);
23 this.OnValueChanged();
24 }
25 public void OnGreenSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
26 {
27 this._value = Color.FromArgb(_value.A, _value.R, Convert.ToByte(e.NewValue), _value.B);
28 this.OnValueChanged();
29 }
30 public void OnBlueSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
31 {
32 this._value = Color.FromArgb(_value.A, _value.R, _value.G, Convert.ToByte(e.NewValue));
33 this.OnValueChanged();
34 }
35
36 private void OnValueChanged()
37 {
38 this.Preview.Background = new SolidColorBrush(this._value);
39 this.redValue.Text = this._value.R.ToString();
40 this.greenValue.Text = this._value.G.ToString();
41 this.blueValue.Text = this._value.B.ToString();
42 }
43 }
44}
代码很简单,无非就是实现了XAML中声明调用的方法,然后同步几个对象的属性。2using System.Windows;
3using System.Windows.Controls;
4using System.Windows.Data;
5using System.Windows.Documents;
6using System.Windows.Media;
7using System.Windows.Shapes;
8
9namespace Freezed
10{
11 public partial class Window1 : Window
12 {
13 public Window1()
14 {
15 InitializeComponent();
16 }
17
18 Color _value = Colors.Black;
19
20 public void OnRedSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
21 {
22 this._value = Color.FromArgb(_value.A, Convert.ToByte(e.NewValue), _value.G, _value.B);
23 this.OnValueChanged();
24 }
25 public void OnGreenSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
26 {
27 this._value = Color.FromArgb(_value.A, _value.R, Convert.ToByte(e.NewValue), _value.B);
28 this.OnValueChanged();
29 }
30 public void OnBlueSliderChanged(Object sender, RoutedPropertyChangedEventArgs<double> e)
31 {
32 this._value = Color.FromArgb(_value.A, _value.R, _value.G, Convert.ToByte(e.NewValue));
33 this.OnValueChanged();
34 }
35
36 private void OnValueChanged()
37 {
38 this.Preview.Background = new SolidColorBrush(this._value);
39 this.redValue.Text = this._value.R.ToString();
40 this.greenValue.Text = this._value.G.ToString();
41 this.blueValue.Text = this._value.B.ToString();
42 }
43 }
44}
写到这里,发现这一章好像和“数据与表示分离”主题没有太多的关系。先作为预备知识,大家了解一下吧。因为后面的文章都会基于XAML来讨论。
XAML是WPF的核心概念之一。但是,很多人看到这里都会有这样的疑问:为什么要XAML?
微软引入XAML的一个主要的原因就是为了将界面和业务逻辑进行分离。在不远的将来,一个软件公司必须包含两类技术人才,一类是开发人员,而另一类就是设计人员。开发人员负责实现具体的业务逻辑,而设计人员仅仅进行UI的设计。他们的主要机能就是能够很好的操控XAML(与现在的WEB开发有点类似)。同时,微软正在开发新的设计器来实现可视化UI设计(花哨的就象Photoshop和CorelDraw)。
下面引用一篇老外的Blog供大家参考:If xaml is so easy, why is code so hard?
下一篇Blog中,我们将讨论如何使用Binding实现数据与表示的分离。
本文的例子:ColorPicker1.rar
注意:本文相关的例子都在WinFX 11月CTP下测试通过,因为目前环境限制,没能再12月CTP下进行测试,请见谅。