综述
这次主要分三个模块来讲述:
-
UWP简述
这里的简述就不去谈它的定义、生命周期、设计规范等内容了,这些文档写得很全。我会在这里说一说我的一个观点,这个观点也许可以帮助你理清一些关于UWP的困惑。 -
学习阶段
UWP涵盖内容颇多颇广,这里自然无法面面俱到。所以我就把控件这一块提出来重点说一说。我会介绍控件的基础分类以及学习阶段。 -
两个重要的库
相比起其它的开发平台,UWP的库其实要简单很多,最为核心的有两个库,Windows Community Toolkit和WinUI,这里会对它们做一个简单的介绍。
UWP简述
UWP全称是Universal Windows Platform,它是一个平台。而在我们应用开发的语境中,它是一种应用模型(App Model)。
相比起经典桌面应用(Win32),UWP的设计思路更先进,能适应更多的平台,更符合Windows应用未来的需要,它有着非常多的优点,包括但不限于:
- 安全和相对完善的隐私保护
- 理论上可以运行在所有Windows10系统及衍生子系统之上
- 可以访问设备定制的功能
- UI自适应,在不同DPI的设备上都有着相对不错的体验
- 更丰富的交互方式。基于Windows10,我们可以使用磁贴、吐司通知等系统功能来丰富我们的交互体验
听起来这很不错,不过理想很丰满,现实很骨感。自2015年发布以来,UWP应用开发的热度不能说持续低迷,但一直不温不火。所以落到实处,我们来看看UWP应用到底是什么。
从我的角度来看,UWP应用就是一个能跑在桌面设备上的手机应用。
这可能和我们现在的直觉不太相符,毕竟我们做的UWP应用都是面向桌面的。
但在Windows 10刚推出那会儿,也就是2015-2016年间,Windows 10 Mobile还是有一定的市场占有率的。

Windows 10的设计目标是要统一桌面、手机、嵌入式系统和其它平台(比如Xbox, Hololens等)。 但对于普通用户来说,最重要的其实就是桌面和手机平台。而Windows桌面有相对完整的Win32应用生态,可手机上不能直接移植Win32生态,正缺UWP应用来填补空白,所以手机才是UWP应用的主要发力点。
当时的UWP应用主要为手机来设计是完全可以理解的。但问题在于,Windows 10 Mobile死的太快了,2015年发布,2016年开始大面积推送,2017年就宣布停止新功能的开发进入维护阶段。
是所谓“臣等正欲死战,陛下何故先降”。这就让还处于起步阶段的UWP非常难受,同时也遗留了大量的设计问题,使得后来的UWP开发者对于某些设计感到极为困惑。
所以在学习、探索UWP开发的过程中,意识到UWP的很多设计源于手机应用,这一点是很有必要的。
举个例子。
UWP的文件管理对很多初上手的开发者来说都是一种难以理解的迷惑行为。
在Windows 10中,当你打开文件管理器,快速访问里总是有几个系统帮你创建的专用文件夹,比如照片、音乐、视频等,UWP访问这些文件夹非常方便,尽管有些文件夹需要勾选一些权限,但获取这些文件夹不过就是一行代码的事儿。
StorageFolder doumentFolder = KnownFolders.DocumentsLibrary;
但在桌面系统中,我们通常会怎么存文件?新买了一台电脑,电脑有1T固态硬盘,我们很多人都会将它分成CDEF等几个硬盘分区,有的装文档,有的装游戏。
总而言之,我们在桌面上习惯将文件放在我们自己创建的文件夹中,这才是普遍的情况。
而UWP对于这些路径随意的文件,唯一可行的解决方法就是调用文件选取器让用户自己手动去选。还有另外一种要特殊权限的方法,也要用户参与,这里不提。
这就让很多原来的桌面开发者感到困惑,为啥UWP不能直接通过路径访问文件呢?
这时候我们换个思路,如果在手机上会是什么情况呢?
我们有谁会给手机的存储空间分区吗?手机上是不是都有一些公用的文件夹,比如相册、音乐库、下载文件夹?使用频率非常高。
手机上的应用访问文件的权限也是受到严格限制的,大家都是沙盒应用,访问文件也都需要用户的授权,不能随意访问。
可以说,UWP关于文件管理的行为放在手机设备上是理所应当且正常的。但在桌面上就跟原有的文件管理系统格格不入了。
所以当你在开发UWP的过程中碰到了一些棘手的问题,想一下UWP的设计初衷,也许不会帮助你解决问题,但至少能让你理解这个问题的原因。
UWP的学习路径
UWP涵盖的东西很多,时间有限,我们只选取主干内容来说一说:
UWP应用构成
UWP应用可以用多种语言来写,目前主流的是C#和C++/WinRT, UI则使用XAML构建。 在以前,还可以使用HTML和Javascript来做,但现在不推荐,因为Chakra内核的Edge浏览器已经被取代,相关的JS项目也已经不在Visual Studio 2019中提供了。
在UWP应用开发过程中,最核心的两个东西,一个是控件,另一个是WinRT API。
WinRT API非我所擅长,这里便不深入介绍,只简单提一下:
UWP应用需要通过WinRT来实现本机交互,我们在UWP中所使用的涉及本机交互的API,都是由WinRT所提供。
控件学习路径
把UWP当成一个UI框架来看,它提供了丰富的具备现代UI的控件,且这些控件的样式也易于被自定义。
这些控件可以划分为几个类别:
Name | Relationship | Description |
---|---|---|
FrameworkElement | 派生自UIElement | UIElement封装了一些UI操作事件,比如指针、键盘、触摸等。 FrameworkElement在UIElement的基础上添加了布局相关的API和对数据绑定的支持 |
Panel | 派生自FrameworkElement | Panel类型在FrameworkElement的基础上添加了元素测量及排列子对象等方法。后期当我们要实现自己的排列容器的时候会很有用,比如我们要做一个圆形表盘,就可以用这个创建自定义的Panel |
Control | 派生自FrameworkElement | Control类型就是大多数UI控件的基类了,我们通常会定义一个ControlTemplate来实现它的外观 |
ContentControl | 派生自Control | ContentControl有一个核心的属性就是Content,作为一个object,它可以是任何类型的对象。它的内部可以定义一个ContentPresenter,默认使用它来进行内容展示,我们所熟知的Button, Checkbox等控件都基于它 |
ItemsControl | 派生自Control | ItemsControl通常用于呈现集合项,它有四个标志性的属性:Items表示生成的控件内容集合,ItemsSource表示生成内容的集合,一般是ObservableCollection或者List。ItemsPanel表示控制项布局的容器面板。ItemTemplate表示单个项的DataTemplate |
用户界面就结构来说就是控件的堆砌,这五种基础的控件类型是我们学习了解所有控件的基础。
在此基础上,我们需要对UWP所提供的控件有一个大概的认识。在这方面,推荐使用XAML Controls Gallery应用。

这里面罗列了UWP所提供的绝大部分控件,也包括一些动画效果和一些WinUI控件。关于WinUI,我们之后再说。
这里可以谈一下拿到一个控件之后的学习阶段:
最简单的,我们可以直接使用它,不太需要一些额外的研究,比如TextBlock
。
查看控件的用例
这一点XCG已经提供了。但如果是一些比较复杂的使用场景,比如如何调用打印组件,如何自定义任务栏的右键菜单等,可以在Windows Universal Samples 查找。
涉及控件样式修改
- 对于原生控件,可以在Windows10 SDK的generic.xaml文件中查找
- 对于非原生控件,可以在对应的开源库中查找,比如WinUI或者Windows Community Toolkit.
细分场景的特殊调查
以我遇到的一个问题举例。

有一个TextBlock,我设置了它的VerticalAlignment为Center
,但它在容器中却始终不显示居中,使用调试工具检查控件边界可以发现,TextBlock的部分(红色边框线)相对于容器是居中的,但是其中的文本不居中。
换了一个字体之后,情况又有所不同:

我们可以得到一个结论,就是字体除了影响美观外,也是会实际影响排版的。而TextBlock正好有一个属性:TextLineBounds 。通过将其设置为Tight
可以修复这个问题。
这种细枝末节的问题我们在开发中肯定会遇到,而所谓的开发经验或者说踩坑经验也往往就在这个时候累积。
Windows Community Toolkit 与 WinUI
Windows Community Toolkit
这个是由微软主导的开源项目,里面汇集了社区提供的控件,是UWP原生控件的一大补充。
里面很多的控件和服务已经成为UWP开发必不可少的一部分。
比如对于Toast Notification来说,在以前是通过繁琐的XML来定义通知的样式。

但现在可以通过引入Windows Community Toolkit,来以链式调用或者创建类的方式定义通知,有效降低了开发者的心智负担。

WCT目前非常活跃,如果你需要的某些功能在原生控件中没有提供,不妨来这里看看,其中有很多值得学习的控件。
WinUI
WinUI,你可能感到既熟悉又陌生。我们的UWP项目大多会引入WinUI的nuget包,但它却不仅仅是一个控件库这么简单。
对于WinUI 2.X版本来说,它是给基于XAML的UWP应用提供的控件库,包含了实践Fluent Design的新控件以及对原生控件的翻新。对于WinUI 2.X来说,尽可能地降低版本对控件的影响是它的使命。
尽管UWP是运行在Windows 10上,但Windows 10迭代至今已经有多个版本,每一个新版本都会推出一定量的新API,如果你要使用新的API,意味着要么你放弃旧版本的支持,要么就花力气编写版本适应的代码。
WinUI正试图将控件分离出来进行独立迭代,以此来避免只有在某个特定的系统版本才能使用新控件的尴尬。
我想你们可能也知道WinUI3目前正处于预览状态,但这并不意味着WinUI2就处于一个维护状态了,实际上WinUI2依然承担着非常重要的控件更新工作,比如下面这个:

我们以后都会接触到的原生控件UI变化,目前在WinUI 2.6的预览版本中提供,如果你想尝鲜,可以这样做:
安装WinUI2最新的预览版

在引入资源时指定版本
<XamlControlsResources Version="Latest" xmlns="using:Microsoft.UI.Xaml.Controls" />
这样你就可以提前体验控件的新样式了。
另外需要额外提及的一点就是,对于我们来说,WinUI 2.6是一个非常重要的版本,在这个版本中,我们可以看到,它几乎彻底改写了控件的默认样式,这也是我们以后工作的一个重要组成部分,大家可以对这个库保持一定的关注,以后我们大概率是要跟它打交道的。
对于WinUI 3.X版本来说,情况就发生了极大的变化。WinUI 3正在完成从附属控件库到原生UX框架的转变。新的WinUI 3将作为所有的Windows应用的UI层,不论是Win32还是UWP。

当然,你可能会好奇,使用WinUI3创建的项目,到底是UWP应用还是Win32应用?
答案是,可以都是。
这是个很有意思的问题。因为WinUI3并不是一个独立存在的项目,在上游,还有一个更大的计划,就是Project Reunion。
尽管UWP在设计上优于Win32应用,但并不意味着Win32应用一无是处。实际上,经过这么多年的沉淀,Win32 API非常强大,很多功能在WinRT中并没有提供。而UWP也有很多优秀的地方,比如数据存储和隐私管理。
Project Reunion正在尝试在API的层面统一这两者。

我们提到的WinUI3负责应用的UI,而从.NET中分拆出的WinRT,比如C++/WinRT,C#/WinRT等则承担本机交互,MSIX-Core负责应用的打包。
如果计划顺利进行,那么理论上,不论是Win32应用还是UWP应用,最后大家的名字都叫Windows应用。