Windows Presentation Foundation 简介
Windows Presentation Foundation (WPF) 是下一代显示系统,用于生成能带给用户震撼视觉体验的 Windows 客户端应用程序。使用 WPF,您可以创建广泛的独立应用程序以及浏览器承载的应用程序。
WPF 的核心是一个与分辨率无关并且基于向量的呈现引擎,旨在利用现代图形硬件的优势。WPF 通过一整套应用程序开发功能扩展了这个核心,这些功能包括 可扩展应用程序标记语言 (XAML)、控件、数据绑定、布局、二维和三维图形、动画、样式、模板、文档、媒体、文本和版式。WPF 包含在 Microsoft .NET Framework 中,使您能够生成融入了 .NET Framework 类库的其他元素的应用程序。
1. 标记和代码隐藏
WPF 为 Windows 客户端应用程序开发提供了更多编程增强功能。一个明显的增强功能就是使用标记和代码隐藏开发应用程序的功能,ASP.NET 开发人员应该熟悉此体验。您通常使用可扩展应用程序标记语言 (XAML) 标记实现应用程序的外观,而使用托管编程语言(代码隐藏)实现其行为。这种外观和行为的分离具有以下优点:
- 降低了开发和维护成本,因为外观特定的标记并没有与行为特定的代码紧密耦合。
- 开发效率更高,因为设计人员可以在开发人员实现应用程序行为的同时实现应用程序的外观。
- 可以使用多种设计工具实现和共享 XAML 标记,以满足应用程序开发参与者的要求:Microsoft Expression Blend 提供了适合设计人员的体验,而 Visual Studio 2005 针对开发人员。
- WPF 应用程序的全球化和本地化大大简化(请参见 WPF 全球化和本地化概述)。
2. 应用程序类
XBAP 和独立应用程序通常非常复杂,需要额外的应用程序范围的服务,包括启动和生存期管理、共享属性以及共享资源。Application 类封装了这些服务以及更多内容,并且只需使用 XAML 即可实现,如下面的示例所示。
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="MainWindow.xaml" />
此标记是独立应用程序的应用程序定义,并指示 WPF 创建一个在应用程序启动时自动打开 MainWindow 的 Application 对象。
3. 数据绑定
创建大多数应用程序的目的是为用户提供查看和编辑数据的方式。对于 WPF 应用程序,存储和访问数据的工作已经由 Microsoft SQL Server 和 ADO.NET 之类的技术提供。访问数据并将数据加载到应用程序的托管对象中后,WPF 应用程序的复杂工作才开始。实质上它涉及到两个步骤:
1. 将数据从托管对象复制到控件中,在控件上可以显示和编辑数据。
2. 确保将使用控件对数据进行的更改复制回托管对象。
为了简化应用程序开发,WPF 提供了一个数据绑定引擎以自动执行这些步骤。数据绑定引擎的核心单元是 Binding 类,它的任务是将控件(绑定目标)绑定到数据对象(绑定源)。下图说明了这种关系。
下面的示例演示如何将 TextBox 绑定到自定义 Person 对象的实例。下面的代码演示了 Person 的实现。
namespace SDKSample
{
class Person
{
string name = "No Name";
public string Name
{
get { return name; }
set { name = value; }
}
}
}
下面的标记将 TextBox 绑定到自定义 Person 对象的实例。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.DataBindingWindow">
...
<!-- Bind the TextBox to the data source (TextBox.Text to Person.Name) -->
<TextBox Name="personNameTextBox" Text="{Binding Path=Name}" />
...
</Window>
using System.Windows; // Window
namespace SDKSample
{
public partial class DataBindingWindow : Window
{
public DataBindingWindow()
{
InitializeComponent();
// Create Person data source
Person person = new Person();
// Make data source available for binding
this.DataContext = person;
}
}
}
在此示例中,Person 类在代码隐藏中实例化,并被设置为 DataBindingWindow 的数据上下文。在标记中,将 TextBox 的 Text 属性绑定到 Person.Name 属性(使用“{Binding ... }”XAML 语法)。此 XAML 通知 WPF 将 TextBox 控件绑定到存储在窗口的 DataContext 属性中的 Person 对象。
WPF 数据绑定引擎还提供了其他支持,包括验证、排序、筛选和分组。此外,当标准 WPF 控件显示的 UI 不合适时,数据绑定还支持使用数据模板为绑定的数据创建自定义 UI。
4. 控件模板
WPF 控件的默认 UI 通常由其他控件和形状构造而来。
有时控件的默认外观可能与应用程序的整体外观不一致。在这种情况下,您可以使用 ControlTemplate 更改控件的 UI 的外观,而无需更改控件的内容和行为。
例如,下面的示例演示如何使用 ControlTemplate 更改 Button 的外观。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.ControlTemplateButtonWindow"
Title="Button with Control Template" Height="158" Width="290">
<!-- Button using an ellipse -->
<Button Content="Click Me!" Click="button_Click">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Margin="5">
<Ellipse Stroke="DarkBlue" StrokeThickness="2">
<Ellipse.Fill>
<RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Azure" Offset="0.1" />
<GradientStop Color="CornflowerBlue" Offset="1.1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Name="content" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Window>
using System.Windows; // Window, RoutedEventArgs, MessageBox
namespace SDKSample
{
public partial class ControlTemplateButtonWindow : Window
{
public ControlTemplateButtonWindow()
{
InitializeComponent();
}
void button_Click(object sender, RoutedEventArgs e)
{
// Show message box when button is clicked
MessageBox.Show("Hello, Windows Presentation Foundation!");
}
}
}
在此示例中,默认按钮 UI 被替换为 Ellipse,后者具有深蓝色的边框并使用 RadialGradientBrush 进行填充。ContentPresenter 控件显示 Button 的内容,即“Click Me!”。单击 Button 时,仍会作为 Button 控件的部分默认行为引发 Click 事件。下图演示了结果。
5. 数据模板
控件模板使您可以指定控件的外观,数据模板则允许您指定控件内容的外观。数据模板通常用于改进绑定数据的显示方式。
默认外观是您希望 ListBox 具有的外观。但是,每个任务的默认外观只包含任务名称。若要显示任务名称、说明和优先级,必须使用 DataTemplate 更改 ListBox 控件的绑定列表项的默认外观。下面的 XAML 定义了这样一个 DataTemplate,它通过使用 ItemTemplate 属性应用于每个任务。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.DataTemplateWindow"
Title="With a Data Template">
...
<!-- Data Template (applied to each bound task item in the task collection) -->
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="DarkSlateBlue" BorderThickness="2"
CornerRadius="2" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Padding="0,0,5,0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Padding="0,0,5,0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Padding="0,0,5,0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
...
<!-- Data template is specified by the ItemTemplate attribute -->
<ListBox
ItemsSource="{Binding}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"
IsSynchronizedWithCurrentItem="True"
Margin="5,0,5,5" />
...
</Window>
下图演示了此代码的效果。
请注意,ListBox 保留其行为和整体外观;只有列表框显示的内容的外观发生了变化。
6. 样式
开发人员和设计人员使用样式可以对其产品的特定外观进行标准化。WPF 提供了一个强大的样式模型,其基础是 Style 元素。下面的示例创建一个样式,该样式将窗口中的每个 Button 的背景色设置为 Orange。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.StyleWindow"
Title="Styles">
...
<!-- Style that will be applied to all buttons -->
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Orange" />
<Setter Property="BorderBrush" Value="Crimson" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="5" />
</Style>
...
<!-- This button will have the style applied to it -->
<Button>Click Me!</Button>
<!-- This labe will not have the style applied to it -->
<Label>Don't Click Me!</Label>
<!-- This button will have the style applied to it -->
<Button>Click Me!</Button>
...
</Window>
由于此样式针对所有 Button 控件,因此它自动应用于窗口中的所有按钮,如下图所示。
7. 资源
一个应用程序中的各控件应共享相同的外观,包括从字体和背景色到控件模板、数据模板和样式的所有方面。您可以使用 WPF 对 用户界面 (UI) 资源的支持将这些资源封装到一个位置,以便于重复使用。
下面的示例定义一个通用的由 Button 和 Label 共享的背景色。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.ResourcesWindow"
Title="Resources Window">
<!-- Define window-scoped background color resource -->
<Window.Resources>
<SolidColorBrush x:Key="defaultBackground" Color="Red" />
</Window.Resources>
...
<!-- Button background is defined by window-scoped resource -->
<Button Background="{StaticResource defaultBackground}">One Button</Button>
<!-- Label background is defined by window-scoped resource -->
<Label Background="{StaticResource defaultBackground}">One Label</Label>
...
</Window>
此示例使用 Window.Resources 属性元素实现背景色资源。此资源可用于 Window 的所有子项。资源范围有多种,包括下面按解析顺序列出的范围:
1. 单个控件(使用继承的 FrameworkElement..::.Resources 属性)。
2. Window 或 Page(也使用继承的 FrameworkElement..::.Resources 属性)。
3. Application(使用 Application..::.Resources 属性)。
范围的多样性使您可以灵活选择定义和共享资源的方式。
作为将资源与特定范围直接关联的一个备用方法,您可以使用单独的 ResourceDictionary(可以在应用程序的其他部分引用)打包一个或多个资源。例如,下面的示例在资源字典中定义默认背景色。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Define background color resource -->
<SolidColorBrush x:Key="defaultBackground" Color="Red" />
<!-- Define other resources -->
...
</ResourceDictionary>
下面的示例引用上一个示例中定义的资源字典,从而在应用程序中共享。
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BackgroundColorResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
...
</Application>
资源和资源字典是 WPF 支持主题和外观的基础。
8. 主题和外观
WPF 中的主题和外观都可以使用资源字典非常轻松地进行定义。下面的示例演示示例外观定义。
<!-- Blue Skin -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue" />
</Style>
...
</ResourceDictionary>
9. 自定义控件
尽管 WPF 提供了大量自定义项支持,您仍然可能会遇到现有 WPF 控件不能满足应用程序或用户需求的情况。在以下情况下可能会出现这种情形:
- 无法通过自定义现有 WPF 实现的外观来创建您需要的 UI。
- 现有 WPF 实现不支持(或很难支持)您需要的行为。
但是,目前您可以利用三个 WPF 模型之一创建一个新的控件。每个模型都针对一个特定的方案,并要求您的自定义控件从特定的 WPF 基类派生而来。此处列出了此三个模型:
- 用户控件模型。从 UserControl 派生的自定义控件,由其他一个或多个控件组成。
- 控制模型。从 Control 派生的自定义控件,用于生成使用模板将其行为和外观相分离的实现,与多数 WPF 控件非常相似。从 Control 派生使您可以比用户控件更自由地创建自定义 UI,但可能需要投入更多精力。
- 框架元素模型。从 FrameworkElement 派生的自定义控件,其外观由自定义呈现逻辑(而不是模板)定义。
下面的示例演示从 UserControl 派生的自定义数值 up/down 控件。
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NumericUpDown">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- Value text box -->
<Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
<!-- Up/Down buttons -->
<RepeatButton Name="upButton" Click="upButton_Click" Grid.Column="1"
Grid.Row="0">Up</RepeatButton>
<RepeatButton Name="downButton" Click="downButton_Click" Grid.Column="1"
Grid.Row="1">Down</RepeatButton>
</Grid>
</UserControl>
using System; // EventArgs
using System.Windows; // DependencyObject, DependencyPropertyChangedEventArgs,
// FrameworkPropertyMetadata, PropertyChangedCallback,
// RoutedPropertyChangedEventArgs
using System.Windows.Controls; // UserControl
namespace SDKSample
{
public partial class NumericUpDown : UserControl
{
// NumericUpDown user control implementation
...
}
}
下一个示例演示将用户控件与 Window 合并所需的 XAML。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.UserControlWindow"
xmlns:local="clr-namespace:SDKSample"
Title="User Control Window">
...
<!-- Numeric Up/Down user control -->
<local:NumericUpDown />
...
</Window>
下图演示 Window 中承载的 NumericUpDown 控件。