WPF(Windows Presentation Foundation)入门指南
WPF(Windows Presentation Foundation)入门指南
WPF(Windows Presentation Foundation)是微软开发的一个用于构建桌面应用程序的UI框架。它使用基于矢量的呈现引擎,能够充分利用现代图形硬件,提供丰富的应用程序开发功能。本文将详细介绍WPF的主要特性和使用方法,包括XAML标记、代码隐藏、数据绑定、图形和动画等核心概念。
什么是WPF
WPF(Windows Presentation Foundation)是一个与分辨率无关的UI框架,使用基于矢量的呈现引擎,构建用于利用现代图形硬件。WPF提供一套完善的应用程序开发功能,这些功能包括Extensible Application Markup Language (XAML)、控件、数据绑定、布局、二维和三维图形、动画、样式、模板、文档、媒体、文本和版式。WPF属于.NET,因此可以生成整合.NET API其他元素的应用程序。
WPF有两种实现:
- .Net版本(本指南):GitHub上托管的WPF开源实现,可在.NET 5上运行。适用于XAML设计器最低要求Visual Studio 2019版本16.8。但根据.NET的版本,可能需要使用较新版本的Visual Studio。
- .NET Framework 4版本:受Visual Studio 2019和Visual Studio 2017支持的WPF的.NET Framework实现。
尽管.NET是一种跨平台技术,但WPF仅在Windows上运行。
为何从.NET Framework升级
将应用程序从.NET Framework升级到.NET时,你将受益于:
- 性能更好
- 新的.NET API
- 最新语言改进
- 改进的辅助功能和可靠性
- 更新的工具及其他
使用WPF进行编程
WPF作为.NET类型的一个子集存在,大部分位于System.Windows命名空间中。如果你曾经使用ASP.NET和Windows窗体等框架通过.NET构建应用程序,应该会熟悉基本的WPF编程体验:
- 实例化类
- 设置属性
- 调用方法
- 处理事件
WPF还包括可增强属性和事件的其他编程构造:依赖项属性和路由事件。
标记和代码隐藏
通过WPF,可以使用标记和代码隐藏开发应用程序,这是ASP.NET开发人员已经熟悉的体验。通常使用XAML标记实现应用程序的外观,同时使用托管编程语言(代码隐藏)来实现其行为。这种外观和行为的分离具有以下优点:
- 降低了开发和维护成本,因为特定于外观的标记与特定于行为的代码不紧密耦合。
- 开发效率更高,因为设计人员在实现应用程序外观的同时,开发人员可以实现应用程序的行为。
- WPF应用程序的全球化和本地化得以简化。
标记
XAML是一种基于XML的标记语言,以声明形式实现应用程序的外观。通常用它定义窗口、对话框、页面和用户控件,并填充控件、形状和图形。
下面的示例使用XAML来实现包含一个按钮的窗口的外观:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Window with button"
Width="250" Height="100">
<!-- Add button to window -->
<Button Name="button">Click Me!</Button>
</Window>
具体而言,此XAML使用Window
元素定义窗口,使用Button
元素定义按钮。每个元素均配置了特性(如Window
元素的Title
特性)来指定窗口的标题栏文本。在运行时,WPF会将标记中定义的元素和特性转换为WPF类的实例。例如,Window
元素被转换为Window
类的实例,该类的Title
属性是Title
特性的值。
代码隐藏
应用程序的主要行为是实现响应用户交互的功能。例如,单击菜单或按钮,以及在响应中调用业务逻辑和数据访问逻辑。在WPF中,在与标记相关联的代码中实现此行为。此类代码称为代码隐藏。下面的示例演示上一个示例的更新标记和代码隐藏:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.AWindow"
Title="Window with button"
Width="250" Height="100">
<!-- Add button to window -->
<Button Name="button" Click="button_Click">Click Me!</Button>
</Window>
更新的标记定义xmlns:x
命名空间,并将其映射到为代码隐藏类型添加支持的架构。x:Class
特性用于将代码隐藏类与此特定XAML标记相关联。考虑此特性在<Window>
元素上声明,代码隐藏类必须从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
,因此不需要手动实现它。)
输入和命令
最常检测和响应用户输入的控件。WPF输入系统使用直接事件和路由事件来支持文本输入、焦点管理和鼠标定位。
应用程序通常具有复杂的输入要求。WPF提供了一个命令系统,用于将用户输入操作与对这些操作做出响应的代码分隔开来。命令系统允许多个源调用相同的命令逻辑。例如,进行由不同应用程序使用的常见编辑操作:复制、剪切和粘贴。如果使用命令实现了这些操作,则它们可以由不同的用户操作调用。
控件
应用程序模型带来的用户体验是构造的控件。在WPF中,“控件”是一个概括性术语,适用于具有以下特征的WPF类类别:
- 托管在窗口或页面中。
- 拥有用户界面。
- 实现某些行为。
布局
创建用户界面时,按照位置和大小排列控件以形成布局。任何布局的一项关键要求都是适应窗口大小和显示设置的变化。WPF为你提供一流的可扩展布局系统,而不强制你编写代码以适应这些情况下的布局。
布局系统的基础是相对定位,这提高了适应不断变化的窗口和显示条件的能力。该布局系统还可管理控件之间的协商以确定布局。协商是一个两步过程:首先,控件将需要的位置和大小告知父级。其次,父级将控件可以有的空间告知控件。
该布局系统通过基WPF类公开给子控件。对于通用的布局(如网格、堆叠和停靠),WPF包括若干布局控件:
下面的示例使用DockPanel
布置几个TextBox
控件:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.LayoutWindow"
Title="Layout with the DockPanel" Height="143" Width="319">
<!--DockPanel to layout four text boxes-->
<DockPanel>
<TextBox DockPanel.Dock="Top">Dock = "Top"</TextBox>
<TextBox DockPanel.Dock="Bottom">Dock = "Bottom"</TextBox>
<TextBox DockPanel.Dock="Left">Dock = "Left"</TextBox>
<TextBox Background="White">This TextBox "fills" the remaining space.</TextBox>
</DockPanel>
</Window>
数据绑定
大多数应用程序旨在为用户提供查看和编辑数据的方法。对于WPF应用程序,存储和访问数据的工作已由许多不同的.NET数据访问库(例如SQL和Entity Framework Core)提供。访问数据并将数据加载到应用程序的托管对象后,WPF应用程序的复杂工作开始。从根本上来说,这涉及到两件事:
- 将数据从托管对象复制到控件,在控件中可以显示和编辑数据。
- 确保使用控件对数据所做的更改将复制回托管对象。
为了简化应用程序开发,WPF提供了一个强大的数据绑定引擎来自动处理这些步骤。数据绑定引擎的核心单元是Binding
类,其工作是将控件(绑定目标)绑定到数据对象(绑定源)。
WPF支持直接在XAML标记中声明绑定。例如,下面的XAML代码使用“Text”XAML语法将TextBox
的Name
属性绑定到对象的{Binding ... }
属性。这假设有一个数据对象设置为具有DataContext
属性Window
的Name
属性。
<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>
图形和动画
WPF提供一组广泛且灵活的图形功能,具有以下优点:
- 图形与分辨率和设备均无关。WPF图形系统中的基本度量单位是与设备无关的像素(即1/96英寸),为实现与分辨率和设备无关的呈现提供了基础。每个与设备无关的像素都会自动缩放,以匹配呈现它的系统的每英寸点数(dpi)设置。
- 精度更高。WPF坐标系统使用双精度浮点数字度量,而不是单精度数字。转换和不透明度值也表示为双精度数字。WPF还支持广泛的颜色域(scRGB),并集成了对管理来自不同颜色空间的输入的支持。
- 高级图形和动画支持。WPF通过为你管理动画场景简化了图形编程,你无需担心场景处理、呈现循环和双线性内插。此外,WPF还提供了点击测试支持和全面的alpha合成支持。
- 硬件加速。WPF图形系统充分利用图形硬件来尽量降低CPU使用率。
2D图形
WPF提供一个常用矢量绘制的二维形状库,例如矩形和椭圆。形状不只是用于显示;还会实现许多你期望的控件功能,包括键盘和鼠标输入。
三维呈现
WPF还包括三维呈现功能,这些功能与二维图形集成,以创建更精彩、更有趣的用户界面。
动画
WPF动画支持可以使控件变大、抖动、旋转和淡出,以形成有趣的页面过渡等。你可以对大多数WPF类,甚至自定义类进行动画处理。
文本和版式
WPF提供以下功能以实现高质量的文本呈现:
- OpenType字体支持。
- ClearType增强功能。
- 利用硬件加速的高性能。
- 文本与媒体、图形和动画的集成。
- 国际字体支持和回退机制。
自定义WPF应用
内容模型
大多数WPF控件的主要用途是显示内容。在WPF中,可以构成控件内容的项的类型和数目称为控件的内容模型。某些控件可以包含一种内容类型的一个项。例如,TextBox
的内容是分配给Text
属性的一个字符串值。
但是,其他控件可以包含不同内容类型的多个项;Button
的内容(由Content
属性指定)可以包含各种项,包括布局控件、文本、图像和形状。
触发器
尽管XAML标记的主要用途是实现应用程序的外观,你也可以使用XAML来实现应用程序行为的某些方面。其中一个示例是使用触发器来基于用户交互更改应用程序的外观。
模板
WPF控件的默认用户界面通常是从其他控件和形状构造的。例如,Button
由ButtonChrome
和ContentPresenter
控件组成。ButtonChrome
提供了标准按钮外观,而ContentPresenter
显示按钮的内容,正如Content
属性所指定。
有时,某个控件的默认外观可能与应用程序的整体外观冲突。在这种情况下,可以使用ControlTemplate
更改控件的用户界面的外观,而不更改其内容和行为。
数据模板
使用控件模板可以指定控件的外观,而使用数据模板则可以指定控件内容的外观。数据模板经常用于改进绑定数据的显示方式。
样式
通过样式功能,开发人员和设计人员能够对其产品的特定外观进行标准化。WPF提供了一个强样式模型,其基础是Style
元素。样式可以将属性值应用于类型。引用样式时,可以根据类型将其自动应用于所有对象,或应用于单个对象。
资源
应用程序中的控件应共享相同的外观,它可以包括从字体和背景色到控件模板、数据模板和样式的所有内容。你可以对用户界面资源使用WPF支持,以将这些资源封装在一个位置以便重复使用。
自定义控件
尽管WPF提供了大量自定义支持,但你仍可能会遇到现有WPF控件不满足你的应用程序或其用户的需求的情况。出现这种情况的原因有:
- 不能通过自定义现有WPF实现的外观和感觉创建所需的用户界面。
- 现有WPF实现不支持(或很难支持)所需的行为。
但是,此时,你可以充分利用三个WPF模型中的一个来创建新的控件。每个模型都针对一个特定的方案并要求你的自定义控件派生自特定WPF基类。下面列出了这三个模型:
- 用户控件模型:自定义控件派生自
UserControl
并由一个或多个其他控件组成。 - 控件模型:自定义控件派生自
Control
,并用于生成使用模板将其行为与其外观分隔开来的实现,非常类似大多数WPF控件。派生自Control
使得你可以更自由地创建自定义用户界面(相较用户控件),但它可能需要花费更多精力。 - 框架元素模型:当其外观由自定义呈现逻辑(而不是模板)定义时,自定义控件派生自
FrameworkElement
。