Windows Presentation Foundation (WPF) 是一个与分辨率无关的 UI 框架,使用基于矢量的呈现引擎,构建用于利用现代图形硬件。 WPF 提供一套完善的应用程序开发功能,这些功能包括 Extensible Application Markup Language (XAML)、控件、数据绑定、布局、二维和三维图形、动画、样式、模板、文档、媒体、文本和版式。 WPF 属于 .NET,因此可以生成整合 .NET API 其他元素的应用程序。
WPF 有两种实现:
将应用程序从 .NET Framework 升级到 .NET 时,你将受益于:
WPF 作为 .NET 类型的一个子集存在,大部分位于System.Windows命名空间中。 如果你曾经使用 ASP.NET 和 Windows 窗体等框架通过 .NET 构建应用程序,应该会熟悉基本的 WPF 编程体验:
WPF 还包括可增强属性和事件的其他编程构造:
依赖项属性和路由事件
。
通过 WPF,可以使用标记和代码隐藏开发应用程序,这是 ASP.NET 开发人员已经熟悉的体验。 通常使用 XAML 标记实现应用程序的外观,同时使用托管编程语言(代码隐藏)来实现其行为。 这种外观和行为的分离具有以下优点:
降低了开发和维护成本,因为特定于外观的标记与特定于行为的代码不紧密耦合。
开发效率更高,因为设计人员在实现应用程序外观的同时,开发人员可以实现应用程序的行为。
XAML 是一种基于 XML 的标记语言,以声明形式实现应用程序的外观。 通常用它定义窗口、对话框、页面和用户控件,并填充控件、形状和图形。
下面的示例使用 XAML 来实现包含一个按钮的窗口的外观:
具体而言,此 XAML 使用 Window
元素定义窗口,使用 Button
元素定义按钮。 每个元素均配置了特性(如 Window
元素的 Title
特性)来指定窗口的标题栏文本。 在运行时,WPF 会将标记中定义的元素和特性转换为 WPF 类的实例。 例如, Window
元素被转换为Window
类的实例,该类的Title
属性是 Title
特性的值。
下图显示上一个示例中的 XAML 定义的用户界面 (UI):
由于 XAML 是基于 XML 的,因此使用它编写的 UI 汇集在嵌套元素的层次结构中,称为元素树
。 元素树提供了一种直观的逻辑方式来创建和管理 UI。
应用程序的主要行为是实现响应用户交互的功能。 例如,单击菜单或按钮,以及在响应中调用业务逻辑和数据访问逻辑。 在 WPF 中,在与标记相关联的代码中实现此行为。 此类代码称为代码隐藏。 下面的示例演示上一个示例的更新标记和代码隐藏:
更新的标记定义 xmlns:x
命名空间,并将其映射到为代码隐藏类型添加支持的架构。 x:Class
特性用于将代码隐藏类与此特定 XAML 标记相关联。 考虑此特性在
元素上声明,代码隐藏类必须从 Window
类继承。
using System.Windows;
namespace SDKSample
{
public partial class AWindow : Window
{
public AWindow()
{
// InitializeComponent call is required to merge the UI
// that is defined in markup with this class, including
// setting properties and registering event handlers
InitializeComponent();
}
void button_Click(object sender, RoutedEventArgs e)
{
// Show message box when button is clicked.
MessageBox.Show("Hello, Windows Presentation Foundation!");
}
}
}
从代码隐藏类的构造函数调用 InitializeComponent
,以将标记中定义的 UI 与代码隐藏类合并在一起。 (生成应用程序时即会生成 InitializeComponent
,因此不需要手动实现它。)x:Class
和 InitializeComponent
的组合可确保在创建实现时正确地对其进行初始化。
请注意,在标记中, 元素定义了
Click
属性的值 button_click
。 将标记和代码隐藏初始化并使其一起工作后,按钮的
Click事件会自动映射到 button_click
方法。 单击该按钮时,将调用事件处理程序,并通过调用
System.Windows.MessageBox.Show方法显示一个消息框。
下图显示单击该按钮后的结果:
最常检测和响应用户输入的控件。 WPF 输入系统 使用直接事件和路由事件来支持文本输入、焦点管理和鼠标定位。
应用程序通常具有复杂的输入要求。 WPF 提供了命令系统,用于将用户输入操作与对这些操作做出响应的代码分隔开来。 命令系统允许多个源调用相同的命令逻辑。 例如,进行由不同应用程序使用的常见编辑操作:复制、剪切和粘贴。 如果使用命令实现了这些操作,则它们可以由不同的用户操作调用。
应用程序模型带来的用户体验是构造的控件。 在 WPF 中,“控件”是一个概括性术语,适用于具有以下特征的 WPF 类类别:
下面列出了内置的 WPF 控件:
按钮:
Button 和
RepeatButton。
数据显示:DataGrid
、
ListView 和 TreeView
。
日期显示和选项: Calendar
和 DatePicker
。
对话框:OpenFileDialog
、PrintDialog
和
SaveFileDialog。
数字墨迹: InkCanvas
和 InkPresenter
。
文档:DocumentViewer
、FlowDocumentPageViewer
、FlowDocumentReader
、FlowDocumentScrollViewer
和 StickyNoteControl
。
输入:
TextBox、RichTextBox
和PasswordBox
。
布局:Border
、BulletDecorator
、Canvas
、DockPanel
、Expander
、
Grid、 GridView
、GridSplitter
、GroupBox
、
Panel、ResizeGrip
、Separator
、
ScrollBar、ScrollViewer
、StackPanel
、
Thumb、Viewbox
、VirtualizingStackPanel
、Window
和WrapPanel
。
媒体:Image
、MediaElement
和SoundPlayerAction
。
菜单:ContextMenu
、Menu
和ToolBar
。
导航:
Frame、Hyperlink
、 Page
、NavigationWindow
和 TabControl
。
选项:CheckBox
、ComboBox
、ListBox
、
RadioButton和
Slider。
用户信息:AccessText
、
Label、
Popup、 ProgressBar
、
StatusBar、
TextBlock和
ToolTip。
创建用户界面时,按照位置和大小排列控件以形成布局。 任何布局的一项关键要求都是适应窗口大小和显示设置的变化。 WPF 为你提供一流的可扩展布局系统,而不强制你编写代码以适应这些情况下的布局。
布局系统的基础是相对定位,这提高了适应不断变化的窗口和显示条件的能力。 该布局系统还可管理控件之间的协商以确定布局。 协商是一个两步过程:首先,控件将需要的位置和大小告知父级。 其次,父级将控件可以有的空间告知控件。
该布局系统通过基 WPF 类公开给子控件。 对于通用的布局(如网格、堆叠和停靠),WPF 包括若干布局控件:
Canvas
:子控件提供其自己的布局。
DockPanel:子控件与面板的边缘对齐。
Grid:子控件由行和列定位。
StackPanel:子控件垂直或水平堆叠。
VirtualizingStackPanel
:子控件在水平或垂直的行上虚拟化并排列。
WrapPanel
:子控件按从左到右的顺序放置,在当前行上的空间不足时换行到下一行。
下面的示例使用
DockPanel 布置几个 TextBox
控件:
Dock = "Top"
Dock = "Bottom"
Dock = "Left"
This TextBox "fills" the remaining space.
DockPanel 允许子
TextBox 控件,以告诉它如何排列这些控件。 为了完成此操作,
DockPanel 实现 Dock
附加了属性,该属性公开给子控件,以允许每个子控件指定停靠样式。
由父控件实现以便子控件使用的属性是 WPF 构造,称为附加属性
。
下图显示上一个示例中的 XAML 标记的结果::
大多数应用程序旨在为用户提供查看和编辑数据的方法。 对于 WPF 应用程序,存储和访问数据的工作已由许多不同的 .NET 数据访问库(例如 SQL 和 Entity Framework Core)提供。 访问数据并将数据加载到应用程序的托管对象后,WPF 应用程序的复杂工作开始。 从根本上来说,这涉及到两件事:
为了简化应用程序开发,WPF 提供了一个强大的数据绑定引擎来自动处理这些步骤。 数据绑定引擎的核心单元是
Binding类,其工作是将控件(绑定目标)绑定到数据对象(绑定源)。 下图阐释了这种关系:
WPF 支持直接在 XAML 标记中声明绑定。 例如,下面的 XAML 代码使用“{Binding ... }
”XAML 语法将
TextBox的
Text属性绑定到对象的 Name
属性。 这假设有一个数据对象设置为具有 Name
属性 Window
的
DataContext属性。
WPF 数据绑定引擎不仅提供绑定,还提供验证、排序、筛选和分组。 此外,数据绑定支持使用数据模板来为数据绑定创建自定义的用户界面。
WPF 提供一组广泛且灵活的图形功能,具有以下优点:
图形与分辨率和设备均无关。 WPF 图形系统中的基本度量单位是与设备无关的像素(即 1/96 英寸),为实现与分辨率和设备无关的呈现提供了基础。 每个与设备无关的像素都会自动缩放,以匹配呈现它的系统的每英寸点数 (dpi) 设置。
精度更高。 WPF 坐标系统使用双精度浮点数字度量,而不是单精度数字。 转换和不透明度值也表示为双精度数字。 WPF 还支持广泛的颜色域 (scRGB),并集成了对管理来自不同颜色空间的输入的支持。
高级图形和动画支持。 WPF 通过为你管理动画场景简化了图形编程,你无需担心场景处理、呈现循环和双线性内插。 此外,WPF 还提供了点击测试支持和全面的 alpha 合成支持。
硬件加速。 WPF 图形系统充分利用图形硬件来尽量降低 CPU 使用率。
WPF 提供一个常用矢量绘制的二维形状库,例如矩形和椭圆。 形状不只是用于显示;还会实现许多你期望的控件功能,包括键盘和鼠标输入。
WPF 提供的二维形状包含基本形状的标准集。 但是,你可能需要创建自定义形状以辅助改进自定义用户界面的设计。 WPF 提供几何图形来创建可直接绘制、用作画笔或用于剪辑其他形状和控件的自定义形状。
WPF 二维功能的子集包括视觉效果,如渐变、位图、绘图、用视频绘画、旋转、缩放和倾斜。 这些效果都是通过画笔实现的。 下图显示了一些示例:
WPF 还包括三维呈现功能,这些功能与二维图形集成,以创建更精彩、更有趣的用户界面。 例如,下图显示呈现在三维形状上的二维图像:
WPF 动画支持可以使控件变大、抖动、旋转和淡出,以形成有趣的页面过渡等。 你可以对大多数 WPF 类,甚至自定义类进行动画处理。 下图显示了运行中的一个简单动画:
WPF 提供以下功能以实现高质量的文本呈现:
作为文本与图形集成的演示,下图显示了文本修饰的应用程序:
到目前为止,你已经了解用于开发应用程序的核心 WPF 构建块:
不过,在创建和管理真正独特且视觉效果非凡的用户体验时,基础知识通常是不够的。 标准的 WPF 控件可能无法与你所需的应用程序外观集成。 数据可能不会以最有效的方式显示。 你应用程序的整体用户体验可能不适合 Windows 主题的默认外观和感觉。
大多数 WPF 控件的主要用途是显示内容。 在 WPF 中,可以构成控件内容的项的类型和数目称为控件的 内容模型。 某些控件可以包含一种内容类型的一个项。 例如,TextBox
的内容是分配给
Text 属性的一个字符串值。
但是,其他控件可以包含不同内容类型的多个项;
Button 的内容(由 Content
属性指定)可以包含各种项,包括布局控件、文本、图像和形状。
尽管 XAML 标记的主要用途是实现应用程序的外观,你也可以使用 XAML 来实现应用程序行为的某些方面。 其中一个示例是使用触发器来基于用户交互更改应用程序的外观。
WPF 控件的默认用户界面通常是从其他控件和形状构造的。 例如,Button
由
ButtonChrome 和
ContentPresenter 控件组成。
ButtonChrome 提供了标准按钮外观,而
ContentPresenter 显示按钮的内容,正如 Content
属性所指定。
有时,某个控件的默认外观可能与应用程序的整体外观冲突。 在这种情况下,可以使用ControlTemplate
更改控件的用户界面的外观,而不更改其内容和行为。
例如,单击 Button
时会引发 Click
事件。 通过更改按钮的模板来显示 Ellipse
形状,控件的可视方位发生了变化,但功能却没有。 你仍可以单击该控件的可视方位,将按预期引发
Click 事件。
使用控件模板可以指定控件的外观,而使用数据模板则可以指定控件内容的外观。 数据模板经常用于改进绑定数据的显示方式。 下图显示 ListBox 的默认外观,它绑定到 Task
对象的集合,其中每个任务都具有名称、描述和优先级:
默认外观是你对 ListBox 的期望。 但是,每个任务的默认外观仅包含任务名称。 若要显示任务名称、描述和优先级,必须使用 ListBox
更改 DataTemplate
控件绑定列表项的默认外观。 下面是一个示例,说明如何应用为 Task
对象创建的数据模板。
ListBox 会保留其行为和整体外观;只有列表框所显示内容的外观发生变化。
通过样式功能,开发人员和设计人员能够对其产品的特定外观进行标准化。 WPF 提供了一个强样式模型,其基础是 Style
元素。 样式可以将属性值应用于类型。 引用样式时,可以根据类型将其自动应用于所有对象,或应用于单个对象。 下面的示例创建一个样式,该样式将窗口上的每个 Button
的背景色设置为 Orange
:
由于此样式针对所有 Button 控件,因此将自动应用于窗口中的所有按钮,如下图所示:
应用程序中的控件应共享相同的外观,它可以包括从字体和背景色到控件模板、数据模板和样式的所有内容。 你可以对用户界面资源使用 WPF 支持,以将这些资源封装在一个位置以便重复使用。
下面的示例定义 Button 和 Label共享的通用背景色:
尽管 WPF 提供了大量自定义支持,但你仍可能会遇到现有 WPF 控件不满足你的应用程序或其用户的需求的情况。 出现这种情况的原因有:
但是,此时,你可以充分利用三个 WPF 模型中的一个来创建新的控件。 每个模型都针对一个特定的方案并要求你的自定义控件派生自特定 WPF 基类。 下面列出了这三个模型:
用户控件模型
自定义控件派生自UserControl
并由一个或多个其他控件组成。
控件模型 自定义控件派生自Control,并用于生成使用模板将其行为与其外观分隔开来的实现,非常类似大多数 WPF 控件。 派生自
Control使得你可以更自由地创建自定义用户界面(相较用户控件),但它可能需要花费更多精力。
框架元素模型。
当其外观由自定义呈现逻辑(而不是模板)定义时,自定义控件派生自
FrameworkElement。