ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WinUI3入门指南:从零构建现代化Windows桌面应用

WinUI3入门指南:从零构建现代化Windows桌面应用 1. 从零开始为什么是WinUI3如果你是一个Windows桌面应用开发者最近可能被一个词频繁刷屏WinUI3。无论是微软的官方文档更新还是社区里技术大佬的分享它都占据了越来越多的版面。但当你真正想上手时面对“WinUI3”、“Windows App SDK”、“Project Reunion”这些名词是不是感觉有点懵不知道从何开始这篇文章我就以一个从WPF和WinForms时代一路走过来的开发者视角带你彻底搞懂WinUI3并手把手带你完成第一个应用的搭建避开我当初踩过的所有坑。简单来说WinUI3是微软为Windows 11及Windows 101809及以上版本打造的全新、高性能、现代化的原生UI框架。它最核心的价值在于“解耦”将UI框架从操作系统版本中剥离出来。这意味着你可以通过NuGet包管理器像使用其他第三方库一样为你的应用独立地更新UI框架而无需等待用户升级整个Windows系统。这解决了传统UWP应用开发中UI特性严重依赖系统版本的最大痛点。那么它和之前的技术栈是什么关系你可以这样理解WinUI 2.x它是UWP应用的一部分UI控件库与系统深度绑定。WinUI 3它是一个独立的、跨桌面应用类型的UI框架。它构成了“Windows App SDK”以前叫Project Reunion的UI层。Windows App SDK是一个统一的开发平台旨在弥合Win32如WPF、WinForms、MFC和UWP之间的鸿沟。开发模型你可以用WinUI 3来构建两种主要类型的应用打包应用 (Packaged)使用MSIX打包享受应用商店分发、自动更新、更安全的沙盒环境等现代应用优势。这是推荐的方式。非打包应用 (Unpackaged)传统的.exe桌面应用部署简单但无法使用MSIX带来的部分高级特性。所以学习WinUI3本质上是在拥抱微软为Windows桌面开发生态规划的未来。它提供了Fluent Design System的原生支持性能优异并且与C#和.NET生态尤其是.NET 5/6/7/8结合得非常好。接下来我们就从环境搭建开始一步步走进WinUI3的世界。2. 环境搭建与项目创建避开第一个“天坑”万事开头难而WinUI3的“开头”就在于环境配置。如果这一步没做对后面会冒出各种诡异错误。我将详细拆解每一步并告诉你哪些地方最容易出问题。2.1 系统与工具链的硬性要求首先你的开发机器必须满足最低要求操作系统Windows 10 版本 1809内部版本 17763或更高版本强烈建议使用Windows 11以获得最佳的开发体验和设计工具支持。Visual Studio 2022这是必须的。社区版免费完全够用。安装时务必勾选以下工作负载“.NET 桌面开发”这是基础。“使用C的桌面开发”WinUI3底层依赖一些C运行时这个工作负载必须安装。“通用Windows平台开发”虽然我们主要开发WinUI3桌面应用但部分工具链如某些设计器仍依赖此负载。这是一个关键点很多人会漏掉导致项目模板不出现或设计器无法工作。Windows SDK安装最新的稳定版Windows SDK例如10.0.22621.0或更高。通常在安装VS2022时它会自动安装一个版本但最好在Visual Studio Installer中确认一下。注意网上有些老教程可能会提到需要开启“开发者模式”。对于WinUI3打包应用这通常是必须的因为要安装开发证书。你可以在Windows设置的“隐私和安全性” - “开发者选项”中打开它。2.2 创建第一个WinUI3项目模板选择详解打开VS2022点击“创建新项目”。在搜索框中输入“WinUI”你会看到好几个模板别晕我帮你理清“空白应用、打包(WinUI 3 in Desktop)”这是我们入门和大多数生产项目的首选。它创建一个使用MSIX打包的、单窗口的WinUI3桌面应用。项目结构清晰包含了应用清单、图标等打包所需的一切。“空白应用、非打包(WinUI 3 in Desktop)”创建传统的.exe应用不包含MSIX打包项目。部署简单适合内部工具或对商店分发无需求的场景。“类库(WinUI 3 in Desktop)”用于创建可复用的WinUI3控件库。“运行时组件(WinUI 3 in Desktop)”用于创建C WinRT运行时组件供C#调用以执行高性能或系统级操作。对于初学者毫不犹豫地选择第一个“空白应用、打包(WinUI 3 in Desktop)”。给你的项目起个名字比如HelloWinUI3选择合适的位置和解决方案名称。点击“创建”后VS会生成一个解决方案里面包含两个项目HelloWinUI3 (Desktop)这是主应用项目包含你的大部分代码App.xaml,MainWindow.xaml等。HelloWinUI3 (Package)这是打包项目负责将主应用打包成MSIX安装包。它引用主项目并包含Package.appxmanifest清单文件。第一个实操心得创建完成后先别急着运行。右键点击解决方案选择“重新生成解决方案”。这一步能确保所有依赖项正确还原和编译可以提前发现一些环境配置问题。如果生成成功恭喜你环境基本没问题了。2.3 项目结构初窥与关键文件让我们快速浏览一下生成的主项目(HelloWinUI3 (Desktop))中的几个关键文件App.xaml和App.xaml.cs应用的入口点。App.xaml.cs中的OnLaunched方法决定了应用启动时创建哪个窗口。MainWindow.xaml和MainWindow.xaml.cs主窗口的UIXAML和后台逻辑C#。HelloWinUI3.csproj项目文件。用文本编辑器打开你会看到它引用了Microsoft.WindowsAppSDK即Windows App SDK的NuGet包。这是WinUI3的命脉所在所有更新都通过更新这个NuGet包版本来实现。现在尝试按下F5运行。你应该能看到一个带有深色标题栏的空白窗口弹出。如果成功你的WinUI3之旅就正式启航了。如果失败最常见的错误是“无法激活Windows运行时”或缺少运行时请回头检查“使用C的桌面开发”工作负载是否安装完整并确保Windows SDK版本匹配。3. XAML布局与核心控件构建UI的基石WinUI3的UI使用XAML来定义这对于来自WPF或UWP的开发者来说非常熟悉。但WinUI3的XAML有它自己的命名空间和一套全新的、现代化的控件库。我们从一个简单的登录界面开始学习最常用的布局面板和控件。3.1 理解命名空间与基础布局打开MainWindow.xaml你会看到顶部的XAML命名空间声明。核心的是xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml这和我们熟悉的WPF/UWP是一样的。所有WinUI3控件都在这个默认的命名空间下。布局是UI的骨架。WinUI3提供了多种布局面板最常用的是Grid网格布局功能最强大。通过定义行(RowDefinitions)和列(ColumnDefinitions)可以创建复杂的自适应界面。它类似于HTML中的CSS Grid。StackPanel栈式面板子元素按水平或垂直方向依次排列。简单快捷适用于线性布局。RelativePanel相对面板通过元素间的相对关系进行布局在某些自适应场景下很灵活但不如Grid直观。Canvas画布提供绝对定位适用于绘图或需要精确控制位置的场景。对于现代响应式布局Grid是绝对的主力。我们来改造MainWindow创建一个居中显示的登录框。首先修改MainWindow.xaml中的Grid为其设置一个背景色并定义一些行和列让内容居中。Grid Background{ThemeResource ApplicationPageBackgroundThemeBrush} Grid.RowDefinitions RowDefinition Height*/ RowDefinition HeightAuto/ RowDefinition Height*/ /Grid.RowDefinitions Grid.ColumnDefinitions ColumnDefinition Width*/ ColumnDefinition WidthAuto/ ColumnDefinition Width*/ /Grid.ColumnDefinitions !-- 登录框容器居中在Grid的第1行第1列索引从0开始 -- Border Grid.Row1 Grid.Column1 Background{ThemeResource CardBackgroundFillColorDefaultBrush} CornerRadius8 Padding32 BorderThickness1 BorderBrush{ThemeResource CardStrokeColorDefaultBrush} StackPanel Spacing16 Width300 TextBlock Text用户登录 FontSize24 FontWeightSemiBold HorizontalAlignmentCenter/ TextBox x:NameUsernameBox Header用户名 PlaceholderText请输入用户名/ PasswordBox x:NamePasswordBox Header密码 PlaceholderText请输入密码/ CheckBox x:NameRememberMeCheckBox Content记住我/ Button x:NameLoginButton Content登录 HorizontalAlignmentStretch ClickLoginButton_Click/ /StackPanel /Border /Grid这段代码做了几件事外层的Grid被分成3行3列中间的行和列索引1自动大小(Auto)四周的行列用*填充这迫使中间的Border容器始终处于屏幕中央。我们用Border包裹了一个StackPanelBorder提供了圆角(CornerRadius)和边框这是实现现代化卡片式设计的常用技巧。StackPanel内部垂直排列了TextBlock标题、TextBox、PasswordBox、CheckBox和Button。Spacing属性设置了子元素间的统一间距非常方便。注意Background和BorderBrush等属性使用了{ThemeResource ...}。这是WinUI3资源系统的关键它能根据系统主题亮色/深色自动切换对应的笔刷是实现深色模式兼容的最简单方式。3.2 核心控件与数据绑定初探上面我们已经用到了几个核心输入控件。WinUI3的控件库是Fluent Design的体现许多控件拥有丰富的视觉状态和动画。TextBox/PasswordBox文本输入。Header属性会在输入框上方显示一个美观的标签。Button按钮。我们为其设置了Click事件处理器LoginButton_Click。CheckBox复选框。现在我们需要在MainWindow.xaml.cs中实现LoginButton_Click事件并引入WinUI3中一个极其重要的概念数据绑定。虽然事件处理简单直接但为了更好的架构如MVVM数据绑定是必学的。首先我们在代码后端Code-behind简单实现事件处理private void LoginButton_Click(object sender, RoutedEventArgs e) { string username UsernameBox.Text; // 注意PasswordBox的密码存储在Password属性不是Text。 string password PasswordBox.Password; bool rememberMe RememberMeCheckBox.IsChecked ?? false; // 这里应该进行实际的验证逻辑例如调用API。 // 此处仅做演示。 if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { // 使用ContentDialog显示提示这是WinUI3推荐的对话框 ShowMessageDialog(提示, 用户名和密码不能为空); } else { ShowMessageDialog(登录成功, $欢迎{username}); } } private async void ShowMessageDialog(string title, string content) { ContentDialog dialog new ContentDialog() { Title title, Content content, CloseButtonText 确定, XamlRoot this.Content.XamlRoot // 必须设置XamlRoot }; await dialog.ShowAsync(); }关键点在WinUI3中显示ContentDialog时必须设置XamlRoot属性否则会抛出异常。这是与UWP不同的一个常见坑。this.Content.XamlRoot获取了当前窗口视觉树的根。接下来我们看看如何用数据绑定来改进它。我们在MainWindow.xaml.cs中创建一个简单的视图模型ViewModel类为了演示直接写在同一个文件里public class LoginViewModel : INotifyPropertyChanged { private string _username; public string Username { get _username; set { if (_username ! value) { _username value; OnPropertyChanged(); } } } private string _password; public string Password { ... } // 类似Username的实现 private bool _rememberMe; public bool RememberMe { ... } // 类似Username的实现 // 实现INotifyPropertyChanged接口 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }然后在MainWindow的构造函数中设置DataContextpublic MainWindow() { this.InitializeComponent(); this.DataContext new LoginViewModel(); }最后修改XAML将控件绑定到ViewModel的属性TextBox Header用户名 PlaceholderText请输入用户名 Text{x:Bind PathViewModel.Username, ModeTwoWay, UpdateSourceTriggerPropertyChanged}/ PasswordBox Header密码 PlaceholderText请输入密码 Password{x:Bind PathViewModel.Password, ModeTwoWay}/ CheckBox Content记住我 IsChecked{x:Bind PathViewModel.RememberMe, ModeTwoWay}/这里我们用了{x:Bind}它是WinUI3/UWP中高性能的编译时绑定。ModeTwoWay表示双向绑定。UpdateSourceTriggerPropertyChanged使得文本框每输入一个字符就更新源属性这对于实时验证很有用。第二个实操心得在WinUI3中{x:Bind}默认是OneTime模式如果你需要响应UI变化更新后端数据或者后端数据变化更新UI务必记得设置ModeTwoWay。对于PasswordBox直接绑定到Password属性是可行的但请注意安全内存中的密码字符串需妥善处理。4. 样式、资源与主题打造一致视觉体验一个专业的应用离不开统一的视觉风格。WinUI3通过资源字典和主题系统让样式管理变得非常高效。4.1 使用系统主题资源与自定义样式我们之前已经用到了{ThemeResource ...}。WinUI3内置了大量这样的资源涵盖了颜色、笔刷、字体、动画等。你可以在XAML设计器的“文档大纲”窗口中浏览“资源”选项卡或者在微软官方文档中查找完整的主题资源列表。例如ApplicationPageBackgroundThemeBrush是应用页面的默认背景色笔刷亮色下是白色深色下是深灰色。CardBackgroundFillColorDefaultBrush是卡片控件的默认填充笔刷。自定义样式假设我们想让我们应用中的所有按钮都有一个统一的圆角和背景色。我们可以在App.xaml的Application.Resources中定义样式使其全局生效。Application.Resources ResourceDictionary ResourceDictionary.MergedDictionaries !-- 引入WinUI3默认主题资源这是基础 -- XamlControlsResources xmlnsusing:Microsoft.UI.Xaml.Controls / /ResourceDictionary.MergedDictionaries !-- 自定义一个Button样式 -- Style x:KeyPrimaryButtonStyle TargetTypeButton Setter PropertyBackground Value{ThemeResource AccentFillColorDefaultBrush}/ Setter PropertyForeground Value{ThemeResource TextOnAccentFillColorPrimaryBrush}/ Setter PropertyCornerRadius Value4/ Setter PropertyPadding Value12,8/ Setter PropertyHorizontalAlignment ValueCenter/ /Style /ResourceDictionary /Application.Resources然后在任意按钮上使用这个样式Style{StaticResource PrimaryButtonStyle}。4.2 实现深色/浅色主题切换WinUI3对深色模式的支持是开箱即用的前提是你正确地使用了ThemeResource。但有时我们需要给用户一个手动切换的选项。首先在MainWindow.xaml中添加一个切换按钮ToggleSwitch x:NameThemeToggle OffContent浅色 OnContent深色 ToggledThemeToggle_Toggled/在MainWindow.xaml.cs中处理切换事件private void ThemeToggle_Toggled(object sender, RoutedEventArgs e) { var toggle sender as ToggleSwitch; if (toggle ! null) { // 获取当前窗口的根Frame或Window本身的RequestedTheme if (toggle.IsOn) { // 设置为深色 (this.Content as FrameworkElement).RequestedTheme ElementTheme.Dark; } else { // 设置为浅色 (this.Content as FrameworkElement).RequestedTheme ElementTheme.Light; } // 你也可以设置Application.Current.RequestedTheme来改变整个应用的主题 // Application.Current.RequestedTheme toggle.IsOn ? ApplicationTheme.Dark : ApplicationTheme.Light; } }第三个实操心得RequestedTheme可以在Application、FrameworkElement如Page, Grid等不同层级设置。子元素会继承父元素的主题但也可以被覆盖。设置Application.Current.RequestedTheme会影响整个应用但用户系统的主题偏好设置可能会覆盖它除非你设置为ElementTheme.Default以外的值。更常见的做法是在应用设置中保存用户的选择并在应用启动时应用。5. 导航、打包与部署从开发到发布一个完整的应用通常不止一个页面。WinUI3推荐使用Frame和Page来实现导航。5.1 使用Frame进行页面导航创建新页面在解决方案资源管理器中右键点击主项目 - 添加 - 新建项 - 选择“空白页(WinUI 3)”命名为HomePage.xaml。用同样的方式再创建一个SettingsPage.xaml。修改主窗口结构修改MainWindow.xaml将原来的内容替换为一个NavigationView控件这是WinUI3中实现现代化导航窗体的标准控件。NavigationView x:NameNavView PaneDisplayModeLeftCompact IsBackButtonVisibleCollapsed IsSettingsVisibleTrue ItemInvokedNavView_ItemInvoked NavigationView.MenuItems NavigationViewItem IconHome Content首页 Taghome/ NavigationViewItem IconSetting Content设置 Tagsettings/ /NavigationView.MenuItems Frame x:NameMainFrame/ /NavigationView实现导航逻辑在MainWindow.xaml.cs中处理NavigationView的ItemInvoked事件并根据Tag导航到不同的Page。private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) { if (args.InvokedItemContainer is NavigationViewItem item item.Tag is string tag) { switch (tag) { case home: MainFrame.Navigate(typeof(HomePage)); break; case settings: MainFrame.Navigate(typeof(SettingsPage)); break; default: break; } } } // 在窗口加载时默认导航到首页 protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); MainFrame.Navigate(typeof(HomePage)); }这样一个带有侧边导航栏的应用骨架就搭建好了。NavigationView会自动处理响应式布局、汉堡菜单、选中状态等复杂交互。5.2 应用打包与发布我们的项目是“打包应用”最终产物是.msix或.msixbundle安装包。打包项目(HelloWinUI3 (Package))就是负责这个的。配置应用清单双击打包项目下的Package.appxmanifest文件。这里需要配置“应用程序”选项卡设置显示名称、默认语言、描述等。“视觉资产”选项卡设置各种尺寸的应用图标。这是必须的否则打包会失败或图标显示为默认方块。VS提供了生成所有尺寸图标的功能你需要准备一个至少256x256的Logo。“功能”选项卡声明你的应用需要访问的系统能力如互联网、文件系统、地理位置等。遵循最小权限原则只勾选必需的。“打包”选项卡设置包名称、发布者信息需要从微软开发者中心获取证书和版本号。生成安装包在解决方案配置管理器中选择“Release”和“x64”或你的目标架构。右键点击打包项目 - “发布” - “创建应用包”。跟随向导你可以选择将包上传到微软商店或者生成一个用于旁加载Sideload的本地安装包。旁加载安装如果你生成了本地包你会在输出目录找到.msixbundle文件。双击它即可安装。由于它使用了开发证书你可能需要在“开发者选项”中开启“旁加载应用”。第四个实操心得也是大坑打包时最常见的错误是“证书问题”。对于本地测试VS会自动生成一个临时证书。但如果你更换了开发机器或者证书过期就会导致安装失败。解决方案是在打包项目属性 - “签名”中点击“选择证书” - “创建测试证书...”输入一个密码可留空创建新的。记住分发到其他机器时需要先安装这个证书双击.cer文件到“受信任的根证书颁发机构”否则无法安装应用包。对于商店发布则需要使用从微软合作伙伴中心获取的正式证书。6. 调试技巧与常见问题排查开发过程中难免遇到问题掌握WinUI3特有的调试技巧能事半功倍。XAML Hot Reload这是提升开发效率的神器。在调试运行F5时修改XAML代码并保存UI界面会几乎实时更新无需重启应用。但注意对C#后台逻辑的修改通常需要重启。实时可视化树与实时属性资源管理器在调试运行时VS的“调试” - “窗口”菜单下可以打开这两个工具。它们能让你在运行的应用UI上直接查看元素树、修改属性值对于调试布局和样式问题非常直观。处理异步与线程WinUI3的UI线程是单线程的所有UI操作都必须在主线程Dispatcher上执行。如果你在后台线程如Task.Run中获取了数据需要更新UI必须使用DispatcherQueue// 在非UI线程中 await DispatcherQueue.GetForCurrentThread().TryEnqueue(() { // 这里可以安全地更新UI控件 MyTextBox.Text resultFromBackground; });常见异常处理“The application called an interface that was marshalled for a different thread.”典型的跨线程访问UI错误使用DispatcherQueue解决。“Cannot find a resource with the given key.”检查StaticResource或ThemeResource的Key名是否拼写错误或者资源是否定义在了正确的ResourceDictionary作用域内。“ContentDialog must have a XamlRoot set.”如前所述显示ContentDialog时必须设置其XamlRoot属性为当前窗口或页面的视觉树根。WinUI3的学习曲线初期可能有些陡峭尤其是需要理解Windows App SDK这套新体系。但一旦你熟悉了它的开发模式、掌握了数据绑定、资源系统和导航其开发效率和最终应用的质量与现代化程度是传统Win32框架难以比拟的。从这个小型的登录窗口开始逐步尝试更复杂的布局、自定义控件、本地数据库集成如SQLite with EF Core、网络请求你会发现构建一个功能完整、外观现代的Windows桌面应用从未如此顺畅。
返回列表