1. 不仅是华丽的界面特效
透过简洁的XAML语言,就可以轻松地制作出华丽的效果,这是WPF编程的特色之一。
(图一)一个纯XAML编制的视窗示例(注意:窗口没有引用任何图片)
<!--File: MainWindow.xaml-->
<Window x:Class="WpfDataBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window Title" Height="360" Width="640">
<!--Window Background-->
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Silver" Offset="0.8" />
</LinearGradientBrush>
</Window.Background>
<!--Main Grid-->
<Grid>
<!--The Big Label-->
<Label Content="Hello Wolrd" Foreground="Orange" FontFamily="Verdana" FontSize="72"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" RenderTransformOrigin="0.5, 0.5">
<Label.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Direction="0" ShadowDepth="10" Softness="0.5" />
</Label.BitmapEffect>
<Label.RenderTransform>
<RotateTransform Angle="-20" />
</Label.RenderTransform>
</Label>
</Grid>
</Window>
2. 编程与美工的清晰分离
MainWindow.xaml定义了视窗的图形界面(Graphical User Interface, a.k.a. GUI),MainWindow.xaml.cs定义了界面背后的逻辑处理(Interaction Logic),两个文件描述同一个类(e.g.MainWindow),这是partial class的特征。
//file: MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
namespace WpfDataBindingDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); //initialize components, ui not loaded yet
//print debug message
Debug.Print("Constructor of the partial class MainWindow.");
}
}
}
3. 分离后的整合技术: Data Binding
现在就让Hello World以大概一秒1度的速度顺时针转动,规则是不能直接操作UI控件。建立软件工程的常见分工是这样的:界面设计人员一般不理会界面的实际数据内容是什么(只需知道要什么数据),逻辑编程人员则不用理会界面上有什么控件(只需知道要提供什么数据、如何处理数据)。这样清晰的显示界面与逻辑处理的分离,对软件整体的管理规划、维护升级等都有帮助,算是目前比较先进的软体设计理念。WPF程序的概念本身就是针对这样的架构而产生的。
XAML文件定义用户界面层,CS文件定义逻辑层。将逻辑层产生的数据显示在界面上,这就得依赖DataBinding(数据绑定)。WPF的数据一般绑定于物件的public property(公开属性)之上,这是规定的。被绑的数据可以是任何类型,也可以通过value converter与显示界面进行特定的转换(e.g.boolean <=>color)。另外,被绑定的显示控件之属性必须是DependencyProperty。
(图二)运用INotifyPropertyChanged的PropertyChanged事件触发,通知显示界面进行数据绑定的内容更新。
<!--File: MainWindow.xaml-->
<Window x:Class="WpfDataBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window Title" Height="360" Width="640">
<!--Window Background-->
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Silver" Offset="0.8" />
</LinearGradientBrush>
</Window.Background>
<!--Main Grid-->
<Grid>
<!--The Big Label, DataBinding in Content and its RotationTransform.Angle-->
<Label Content="{Binding RotationAngle}" ContentStringFormat="Hello World {0}" Foreground="Orange" FontFamily="Verdana" FontSize="72"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" RenderTransformOrigin="0.5, 0.5">
<Label.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Direction="0" ShadowDepth="10" Softness="0.5" />
</Label.BitmapEffect>
<Label.RenderTransform>
<RotateTransform Angle="{Binding RotationAngle}" />
</Label.RenderTransform>
</Label>
</Grid>
</Window>
如果你熟悉HTML/XML结构,对上面的XAML代码是驾轻就熟的(不认识的也没关系)。那句{Binding RotationAngle}是WPF数据绑定的核心语法。
//file: MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
namespace WpfDataBindingDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent(); //initialize components, ui not loaded yet
this.DataContext = this; //set self ui data context
//print debug message
Debug.Print("Constructor of the partial class MainWindow.");
//start new thread to do dynamic rotation
new Thread(new ThreadStart(() =>
{
while (true)
{
Thread.Sleep(1000);
RotationAngle = (RotationAngle + 6) % 360; //rotate 6deg every second
}
})).Start();
}
private int m_nAngle = 0;
//public property with property changed notification
public int RotationAngle
{
get { return m_nAngle; }
set
{
m_nAngle = value; //update value
if (null != PropertyChanged) { PropertyChanged(this, new PropertyChangedEventArgs("RotationAngle")); } //notify ui
}
}
public event PropertyChangedEventHandler PropertyChanged; //implement INotifyPropertyChanged interface
}
}
因为MainWindow继承了INotifyPropertyChanged,所以有public event PropertyChangedEventHandler PropertyChanged这么的一个必要成员。PropertyChanged的作用是触发属性已改变的事件信息,让已绑定该属性的显示控件提取最新的数据(类似MFC程序的UpdateData)。换句话说,这个在CS逻辑层的事件触发,能让WPF所对应的数据绑定,在UI线程里适当的的时间进行更新。这个设计的好处在于,我们可以在UI线程(主线程)以外的线程上更新显示控件的内容,而不必切换到该控件所属的线程。
4. 推荐参考网页
WPF的学习亮点是DataBinding,在此推荐下列网页以供参考。