前言
WinUI 基于 Windows App SDK(就是头文件、库和工具集),使用 XAML (前端)和 C# 或 C++ (后端)创建出符合 Windows 用户期望 Fluent Design 外观和风格的应用。
介于这是聪明的巨硬又一次做出的愚蠢决定,以后很可能被抛弃,学习纯属娱乐。

本文为作者跟着官方教程制作一个便签App(WinUINotes)所记录的笔记(放心吧,你可以不用去看官方文档了,我把文档内容中译中了,方便想阅读本文的人理解,一起学习)。
我已经跟着微软的教程成功新建并构建了项目,如果想要使用本文入门WinUI,需要先查看此教程:快速入门:设置环境并创建 WinUI 3 project,通过此教程学习如何利用 Visual Studio 创建项目并构建项目。
其实学习 Visual Studio 并不是错误的选择,这个IDE自带开发环境,帮忙自动配置好,非常方便。
新建项目
打开 Visual Studio ,点击右侧的新建项目按钮

在这里搜索“winui”模板

可以发现有两个模板,简单来说,本文选择 Blank App, Packaged (WinUI 3 in Desktop)(WinUI空白应用(已打包))
- 选择“Blank App, Packaged (WinUI 3 in Desktop)”:单项目,应用和打包配置在一起。
- 选择“Blank App, Packaged with Windows Application Packaging Project”:双项目,应用和打包器分开。
打包器就是把复杂项目所有东西放到一起,统一签名和打包,生成
.msix或.msixbundle安装包。
根据下方图片输入"WinUINotes",然后点击“ 创建”

项目文件用途
点击菜单栏的"视图 - 解决方案管理器"

在解决方案资源管理器中可以看到 WinUI 项目的文件

Assets 文件夹:可以存放图片等资源文件
App.xaml 和 App.xaml.cs :程序入口点(程序的启动逻辑),可以控制全局样式,启动逻辑等。
MainWindow.xaml 和 MainWindow.xaml.cs :应用的主窗口(用户看到的第一个界面),用界面层(XAML 标记语言写)和 逻辑层(C#写)
Package.appxmanifest :Visual Studio 自动生成的清单文件,包项目配置文件(这个不重要)
修改界面
由于我之前写过 HTML ,所以这里用标签指代
<object>方便理解。
打开 MainWindow.xaml 。

可以看到默认是这样的👇
<!--?xml version="1.0" encoding="utf-8"?-->
<window x:class="WinUINotes.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WinUINotes" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d" title="WinUINotes">
<window.systembackdrop>
<micabackdrop>
</micabackdrop></window.systembackdrop>
<grid>
</grid>
</window>
在 MainWindow.xaml ,可以控制窗口的内容,就是图中的这片区域:

窗口的顶部区域由 Windows 系统控制,它包括标题栏,标题控件(最小/最大/关闭按钮)、应用图标、标题和拖动区域。 它还包括包围窗口外侧的框架。

XAML入门语法
众所不周知,HTML实际就是面向对象编程,因为每个元素都可以添加修改属性(在XAML里面,这也是一样的。
既然没学过XML,那就直接理解XAML好了,不用看两者区别了。
这里与HTML及其相似,元素在XAML中叫做标记,大多数标签是成对出现的,即一个开标签(如 <div>)和一个闭标签(如 </div>),自闭合也是可以用的,例如 <Canvas />,它们之间的内容就是元素(标记)的内容。
一个 XAML 元素标签就代表创建一个对象
<!-- 创建一个按钮对象 -->
<Button />
<!-- 创建一个文本框对象 -->
<TextBox />
可以给对象设置属性
XAML 也可以在标签里添加属性,例如简单的属性语法 <object attribute="value"> 也就是 属性名="值" ,如果属性过于复杂或者想要让格式更加整齐,可以使用复杂的属性语法。形式为 <类型.属性名> 嵌套在标签内:
<object>
<object.property>
value
</object.property>
</object>
先不要在语法这里浪费时间与热情,等下边做边学,会讲到的awa
给界面添加一些内容
将 <Window.. > 标签的内容替换为以下代码:
<window.systembackdrop>
<micabackdrop kind="Base">
</micabackdrop></window.systembackdrop>
<grid>
<grid.rowdefinitions>
<!-- Title Bar -->
<rowdefinition height="Auto">
<!-- App Content -->
<rowdefinition height="*">
</rowdefinition></rowdefinition></grid.rowdefinitions>
<titlebar x:name="AppTitleBar" title="WinUI Notes">
<titlebar.iconsource>
<fonticonsource glyph="">
</fonticonsource></titlebar.iconsource>
</titlebar>
<!-- App content -->
</grid>
现在看起来是这样的:

按 Ctrl + S 或点击工具栏中的“保存”图标都可以保存文件。
保存后点击顶部的编译按钮

可以看到成功构建了窗口,窗口也显示了 TitleBar 元素,就是标题栏下方的图标和"WinUI Notes"文字

去掉自带的标题栏
其实,我们是想把这个原本的难看的系统标题栏替换掉,换成 TitleBar 元素内容。
需要在 MainWindow.xaml.cs 写一些代码来替换掉系统标题栏。
打开 MainWindow.xaml.cs .....?这个文件被隐藏了,需要在 MainWindow.xaml 代码编辑窗口中按下 F7 ,或者在编辑窗口右键并选择“查看代码”也是可以的。
其实在解决方案资源管理器展开 MainWindow.xaml 也可以看到 MainWindow.xaml.cs qwq
可以看到 MainWindow.xaml.cs 默认内容是这样的:
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace WinUINotes
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
将 MainWindow 函数的代码替换为:
public MainWindow()
{
this.InitializeComponent();
// ↓ 添加了这些内容 ↓
// 隐藏默认系统标题栏
ExtendsContentIntoTitleBar = true;
// 用 WinUI TitleBar 替换系统标题栏
SetTitleBar(AppTitleBar);
// ↑ 添加了这些内容 ↑
}
这里的 AppTitleBar 对应了之前在 MainWindow.xaml 写的 TitleBar 的参数。

按下 F5 可以直接调试编译()
保存并编译一下,可以看到成功替换了标题栏:

学到这里很厉害啦,可以休息一下了,等会再来学习后面的内容..
创建一个新的页面
现在,我们将创建一个页面,允许用户编辑笔记,然后编写代码以保存或删除笔记。
在解决方案资源管理器的项目名"WinUINotes"处右键点击,在菜单中选择"添加 - 新建项"

点击左下角的"显示所有模板"

选择"空白页"模板,修改名称为 NotePage.xaml,点击添加

给新的页面添加内容
打开 NotePage.xaml ,可以看到默认内容是这样的👇
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="WinUINotes.NotePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUINotes"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
</Grid>
</Page>
将 <Grid> ... </Grid> 元素中的内容替换为以下代码:
<Grid Padding="16" RowSpacing="8">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="NoteEditor"
AcceptsReturn="True"
TextWrapping="Wrap"
PlaceholderText="Enter your note"
Header="New note"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Width="400"
Grid.Column="1"/>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right"
Spacing="4"
Grid.Row="1" Grid.Column="1">
<Button Content="Save" Style="{StaticResource AccentButtonStyle}"/>
<Button Content="Delete"/>
</StackPanel>
</Grid>
现在看起来应该是这样的:

回到 MainWindow.xaml,将 Frame 的内容修改为
<Frame x:Name="rootFrame" Grid.Row="1" SourcePageType="local:NotePage"/>
Frame本身不显示内容,而是负责在多个Page(页面)之间切换。
现在来编译一下看看效果 :)

现在我们可以来研究一下这个页面的构成了。
XAML基础
回顾我们在 MainWindow.xaml 写的代码,可以注意到我们把控件写在了 Grid (网格)元素中:

- Grid:网格(布局容器,用于按行和列排列子元素)
- RowDefinition:行定义(定义网格中某一行的属性,如高度)
可以看到,我们把网格的每一行的属性写在了 <Grid.RowDefinitions> 元素里面,有两个 <RowDefinitions>,也就是创建了2个行。
行是从0开始数的,在子元素中通过 Grid.Row="哪一行" 属性来指定该元素所处的行。可以看看下面代码的注释,方便理解~
<Grid>
<!-- Grid.RowDefinitions 定义网格的行 -->
<Grid.RowDefinitions>
<!-- 标题栏行:高度自适应(Auto) -->
<!-- 从上往下定义,现在规定第0行的高度 -->
<RowDefinition Height="Auto" />
<!-- 内容行:占满剩余的所有空间(*) -->
<!-- 从上往下定义,现在规定第1行的高度 -->
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 以下为子元素(也就是内容) -->
<!-- 现在是第一个子元素,没写Grid.Row属性默认放在第0行 -->
<TitleBar x:Name="AppTitleBar"
Title="WinUI Notes">
<TitleBar.IconSource>
<FontIconSource Glyph=""/>
</TitleBar.IconSource>
</TitleBar>
<!-- 内容 -->
<!-- 现在是第二个子元素,Grid.Row属性为1放在第1行 -->
<Frame x:Name="rootFrame" Grid.Row="1" SourcePageType="local:NotePage"/>
</Grid>
现在再查看 NotePage.xaml 中的代码,同样可以看到我们把所有子元素写在了 Grid (网格)元素中:

- RowDefinition:行定义(定义网格中某一行的属性,如高度)
- Grid.ColumnDefinitions:列定义(定义网格中某一列的属性,如宽度)
可以数一下,有两个 <RowDefinitions> ,三个 <ColumnDefinitions>,所以这个网格规定了 2 行 3 列。
我们给 <RowDefinitions> (行定义)设置了 Height="*" (高度="占满剩余空间")以及 Height="Auto" (高度="高度自适应")属性,给 <ColumnDefinitions> (列定义)设置了 Width="*" (宽度="占满剩余空间")以及 Width="400" (宽度="400")属性。
你可能注意到了,宽度="400"是什么...?
其实这里代表宽度为
400epx,在 XAML 不用 px(像素)来表达布局尺寸和间距,而是使用 epx (有效像素),这个 epx 单位是一个虚拟的度量单位(而 px 是实际存在的物理的像素单位),它会自适应调整间距,只要你设置一个合适的值即可。
通过这张图也可以清晰看到网格的行列,以及每个格子。(顶部的一行是在 MainWindow.xaml 规定的,内容区域的两行是在 NotePage.xaml 规定的)

我们继续研究 NotePage.xaml 。
重要(如果你可以理解)
XAML 命名空间 (xmlns) 映射 是与 C#
using语句对应的 XAML 映射。 在应用项目的 XAML 页面中,为您映射了前缀local:(xmlns:local="using:WinUINotes")。 它被映射到引用为包含x:Class属性和代码而创建的同一命名空间,以便涵盖包括 App.xaml 在内的所有 XAML 文件。 只要在此同一命名空间中定义要在 XAML 中使用的任何自定义类,就可以使用local:前缀来引用 XAML 中的自定义类型。
剩下的内容以后再更细。。。


Comments NOTHING