关于 dotnet-wpf-migration
dotnet-wpf-migration 是帮助团队现代化 Windows 桌面应用的迁移指导技能,涵盖升级到现代 .NET,以及在 WPF、WinUI 3 和 Uno Platform 之间迁移,并依据项目约束梳理路径。
主要功能
- 01WPF .NET Framework 升级到 .NET 8+
- 02WinForms .NET Framework 升级到 .NET 8+
- 03从 WPF 迁移到 WinUI 3
- 04从 WPF 迁移到 Uno Platform
- 05UWP 迁移路径
- 06比较迁移工作量与风险
适用场景
- 01保留界面并升级 WPF 应用
- 02现代化 WinForms 应用运行时
- 03将 WPF 视图改写为 WinUI 3
- 04推动 WPF 代码库支持跨平台目标
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-wpf-migration安装后在 Claude Code 中输入/dotnet-wpf-migration确认已加载。
详细介绍
dotnet-wpf-migration
面向 Windows 桌面应用程序的上下文相关迁移指南。涵盖 WPF .NET Framework 迁移到 .NET 8+、WPF 迁移到 WinUI 3(仅限 Windows 的现代化)、WPF 迁移到 Uno Platform(跨平台)、WinForms .NET Framework 迁移到 .NET 8+、UWP 迁移到 WinUI 3、UWP 迁移到 Uno Platform(交叉引用),以及根据项目约束选择合适迁移目标的决策矩阵。
**版本假设:**以 .NET 8.0+ 为基准(当前 LTS)。自动迁移使用 dotnet-upgrade-assistant。适用时会明确标注 .NET 9 功能。
**范围边界:**此技能负责桌面框架之间的迁移路径,以及从 .NET Framework 迁移到现代 .NET 的路径。各框架的模式(现代 WPF、WinUI、现代 WinForms)由相应的框架技能负责。框架选择决策树由 [skill:dotnet-ui-chooser] 负责。
**范围之外:**WPF .NET 8+ 开发模式——参见 [skill:dotnet-wpf-modern]。WinUI 3 开发模式——参见 [skill:dotnet-winui]。WinForms .NET 8+ 开发模式——参见 [skill:dotnet-winforms-basics]。Uno Platform 开发模式——参见 [skill:dotnet-uno-platform]。框架选择决策树——参见 [skill:dotnet-ui-chooser]。桌面测试——参见 [skill:dotnet-ui-testing-core]。
交叉引用:[skill:dotnet-wpf-modern](WPF .NET 8+ 模式)、[skill:dotnet-winui](WinUI 3 模式)、[skill:dotnet-winforms-basics](WinForms .NET 8+ 模式)、[skill:dotnet-uno-platform](Uno Platform 模式)、[skill:dotnet-ui-chooser](框架选择)、[skill:dotnet-ui-testing-core](桌面测试)。
迁移路径概览
根据当前框架和目标选择迁移路径。每条路径在工作量、风险和能力提升方面各有不同权衡。
| 当前框架 | 目标框架 | 工作量 | 风险 | 适用情况 |
|---|---|---|---|---|
| WPF .NET Framework | WPF .NET 8+ | 低到中 | 低 | 现代化运行时,保留现有 UI |
| WPF .NET Framework | WinUI 3 | 高 | 中 | 采用现代 Windows UI、触控/笔输入和 Fluent |
| WPF .NET Framework | Uno Platform | 高 | 中到高 | 需要跨平台 |
| WinForms .NET Framework | WinForms .NET 8+ | 低 | 低 | 现代化运行时,保留现有 UI |
| UWP | WinUI 3 | 中 | 中 | 保持仅限 Windows,同时使用现代运行时 |
| UWP | Uno Platform | 中到高 | 中 | 需要从 UWP 实现跨平台 |
WPF .NET Framework 迁移到 .NET 8+
风险最低的迁移路径。保留现有 XAML 和代码隐藏,同时迁移到现代 .NET,以获得更好的性能、DI 支持和并行部署能力。
使用 dotnet-upgrade-assistant
.NET Upgrade Assistant 可自动完成大部分迁移工作:
# Install the upgrade assistant
dotnet tool install -g upgrade-assistant
# Analyze the project first (non-destructive)
upgrade-assistant analyze MyWpfApp.sln
# Upgrade the project
upgrade-assistant upgrade MyWpfApp.sln
Upgrade Assistant 可处理的内容:
- 将
.csproj从旧格式转换为 SDK 样式 - 将
packages.config迁移为PackageReference - 将 TFM 更改为
net8.0-windows - 将
AssemblyInfo.cs属性移至.csproj - 替换常见 API 并更新命名空间
需要手动处理的内容:
- 将
App.config设置迁移到appsettings.json或 Host 构建器配置 - 迁移
Settings.settings/My.Settings(VB.NET) - 更新第三方控件库(确认供应商提供的版本兼容 .NET 8)
- 处理 WCF 客户端引用(使用
CoreWCF,或迁移到 gRPC/REST) - 调整 COM 互操作(
System.Runtime.InteropServices中的语法差异) - 自定义 MSBuild 目标和构建脚本
API 兼容性
.NET Framework 和 .NET 8+ 中的大多数 WPF API 都相同。主要差异如下:
| 领域 | .NET Framework | .NET 8+ | 操作 |
|---|---|---|---|
| 剪贴板 | Clipboard.SetText() | API 相同 | 无需更改 |
| 打印 | PrintDialog、PrintVisual | API 相同 | 无需更改 |
| BitmapEffect | 已弃用(由软件渲染) | 已移除 | 使用 Effect / ShaderEffect |
DrawingContext.PushEffect | 可用 | 已移除 | 使用 ShaderEffect |
| XPS 文档 | System.Windows.Xps | 需要 NuGet 包 | 添加 System.Windows.Xps PackageReference |
| 语音合成 | System.Speech | 需要 NuGet 包 | 添加 System.Speech PackageReference |
NuGet 包更新
迁移后,将 NuGet 包更新为兼容 .NET 8 的版本:
# List outdated packages
dotnet list package --outdated
# Update packages (one at a time for safer migration)
dotnet add package Newtonsoft.Json --version 13.*
dotnet add package MaterialDesignThemes --version 5.*
常见的包替换:
Unity容器 ->Microsoft.Extensions.DependencyInjection(内置)Autofac-> 更新到最新版本(支持 .NET 8)log4net/NLog-> 可考虑使用Microsoft.Extensions.Logging搭配 Serilog 或 NLog 提供程序EntityFramework(EF6)->Microsoft.EntityFrameworkCore8.x
重大变更检查清单
- 默认高 DPI 行为已更改。 .NET 8 WPF 默认启用
PerMonitorV2DPI 感知(而非 .NET Framework 中的SystemAware)。 - 可空引用类型。 新项目会启用 NRT。现有代码可能会产生警告。可先通过
<Nullable>disable</Nullable>禁用,然后逐步修复。 - 隐式 using。 新的 SDK 风格项目会启用隐式 using,可能与现有的
using语句冲突。如有需要,可通过<ImplicitUsings>disable</ImplicitUsings>禁用。 - 程序集加载。
AssemblyLoadContext取代AppDomain,用于隔离程序集。使用AppDomain.CreateDomain的插件架构需要重新设计。 - 运行时行为。 .NET 8 的 GC 在 Gen0/Gen1 回收时更加积极。依赖终结器的代码行为可能会有所不同。
有关迁移后的 WPF 模式(Host 构建器、MVVM Toolkit、现代 C#),请参阅 [skill:dotnet-wpf-modern]。
从 WPF 迁移到 WinUI 3
如果需要现代 Windows 原生 UI(Fluent Design、触控/笔输入、Windows 11 集成〔小组件、Mica〕)或在现代 .NET 上使用 UWP 风格的 API,可进行迁移。这需要部分重写——XAML 概念可以沿用,但 API 和命名空间有所不同。
适合采用此迁移路径的情况
- 应用是仅限 Windows的,并且将继续仅支持 Windows
- 需要现代 Fluent Design 控件、触控/笔支持或 Windows 11 功能
- 团队愿意投入 XAML 重写工作
- 应用正在积极开发,并持续添加功能(足以证明这项投入是值得的)
可考虑其他方案的情况
- 如果需要跨平台 -> 改用 Uno Platform(WinUI XAML 表面)
- 如果应用处于维护模式 -> 改用 WPF .NET 8+(工作量较小)
- 如果 WPF Fluent 主题(.NET 9+)已满足需求 -> 继续使用 WPF .NET 8+,并设置
ThemeMode = ThemeMode.System
XAML 差异
| WPF XAML | WinUI 3 XAML | 说明 |
|---|---|---|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | 相同的 URI(但解析为 Microsoft.UI.Xaml 类型,而不是 System.Windows) | 无需更改 xmlns,但运行时类型不同 |
{Binding Path=Name} | {x:Bind Name, Mode=OneWay} | 优先使用 x:Bind(已编译,类型安全) |
DataContext 绑定 | 代码隐藏属性 + x:Bind | x:Bind 根据代码隐藏解析 |
Window 继承自 System.Windows.Window | Window 继承自 Microsoft.UI.Xaml.Window | 基类和 API 不同 |
UserControl | UserControl(Microsoft.UI.Xaml 命名空间) | 概念相同,命名空间不同 |
带有 TargetType 的 Style | 相同 | 用法相同 |
DataTemplate | 使用 x:Bind 时需要 x:DataType | 编译绑定所必需 |
ContextMenu | MenuFlyout | 控件类型不同 |
StatusBar | 无内置等效项 | 使用自定义 CommandBar 或 InfoBar |
RibbonControl | 无内置等效项 | 使用 NavigationView + CommandBar |
迁移策略
- 在现有 WPF 项目旁创建一个新的 WinUI 3 项目
- 首先迁移共享逻辑 ——模型、服务、ViewModel(如果使用 MVVM Toolkit,这些内容可在两者中通用)
- 逐步迁移视图 ——从较简单的页面开始,再迁移复杂页面
- 将 WPF 专用控件替换为 WinUI 对应控件(见上表)
- 将数据绑定从
{Binding}更新为{x:Bind},以确保编译时安全 - 测试 Windows 集成功能(通知、生命周期、文件关联)
- 选择部署模型 ——MSIX(应用标识)或未打包(xcopy)
如需了解 WinUI 3 模式和项目设置,请参阅 [skill:dotnet-winui]。
WPF 到 Uno Platform
当你需要让 WPF 代码库跨平台运行时,可进行迁移。Uno Platform 使用 WinUI XAML API,因此 WPF XAML 技能可以部分迁移,但迁移过程中仍需适配 WinUI XAML 模式。
适用场景
- 应用需要在多个平台上运行(Windows、macOS、Linux、Web、移动设备)
- 团队具备可迁移到 WinUI XAML 的 WPF/XAML 专业知识
- 愿意投入资源进行跨平台适配(平台专属行为、响应式布局)
考虑其他方案的场景
- 如果仅面向 Windows -> 迁移到 WinUI 3(更简单,无跨平台开销)
- 如果应用处于维护模式 -> 迁移到 WPF .NET 8+(所需工作量最少)
- 如果团队具备 Web 技能 -> 考虑使用 Blazor 交付 Web 应用
迁移方法
从 WPF 迁移到 Uno Platform 的方法与迁移到 WinUI 3 类似(因为 Uno 使用 WinUI API),但还需考虑额外的跨平台因素:
- 创建 Uno Platform 项目并指定所需的目标平台
- 迁移 ViewModel 和服务——这些内容与平台无关
- 将 XAML调整为 WinUI 语法(与从 WPF 迁移到 WinUI 时的改动相同)
- 使用条件编译或 Uno 平台扩展处理平台专属功能
- 将仅适用于 Windows 的 API替换为跨平台替代方案(文件对话框、通知等)
- 测试每个目标平台——在 Skia 和原生目标平台上,渲染效果和行为可能有所不同
Uno Extensions 对常见模式的替代方案
Uno Extensions 为常见的 WPF 模式提供跨平台替代方案:
| WPF 模式 | Uno Extensions 替代方案 |
|---|---|
| 自定义导航 | Uno.Extensions.Navigation |
| 手动设置 DI | Uno.Extensions.DependencyInjection(封装 MS DI) |
appsettings.json 配置 | Uno.Extensions.Configuration |
| 手动创建 HTTP 客户端 | Uno.Extensions.Http(使用 Refit 的类型化客户端) |
| 手动序列化 | Uno.Extensions.Serialization |
如需了解 Uno Platform 开发模式,请参阅 [skill:dotnet-uno-platform]。如需了解各目标平台的部署指南,请参阅 [skill:dotnet-uno-targets]。
WinForms .NET Framework 到 .NET 8+
与 WPF 迁移类似,但由于 WinForms 的项目结构不那么复杂,通常更简单。
使用 dotnet-upgrade-assistant
# Analyze first
upgrade-assistant analyze MyWinFormsApp.sln
# Upgrade
upgrade-assistant upgrade MyWinFormsApp.sln
升级助手可处理的内容:
- 将
.csproj转换为 SDK 风格,并添加<UseWindowsForms>true</UseWindowsForms> - 将 TFM 更改为
net8.0-windows - 将
packages.config转换为PackageReference - 将
AssemblyInfo.cs迁移至项目属性
需要手动处理的内容:
- 迁移
App.config/Settings.settings - 将
My.Settings(VB.NET)迁移至现代配置 - 更新第三方控件库(部分 WinForms 控件可能不支持 .NET 8)
- 处理 Crystal Reports 或其他旧版报表工具(评估替代方案)
- 调整 COM 互操作
设计器兼容性
WinForms 设计器文件(.Designer.cs)通常可以顺利迁移。Visual Studio 中适用于 .NET 8+ 的 WinForms 设计器已全面支持。
已知设计器问题:
- 可能需要更新自定义设计器序列化程序
- 部分第三方控件可能无法在 .NET 8 设计器中呈现
- .NET 9+ 提供不感知 DPI 的设计器模式(
<ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>)
迁移后的现代化改造
迁移到 .NET 8+ 后,可考虑逐步采用现代模式:
- 通过 Host builder 添加依赖注入(参见 [skill:dotnet-winforms-basics])
- 将同步调用替换为 async/await
- 启用高 DPI,使用
PerMonitorV2模式 - 启用深色模式(.NET 9+ 中为实验性功能)
有关 WinForms .NET 8+ 模式,请参见 [skill:dotnet-winforms-basics]。
UWP 到 WinUI 3
WinUI 3 是 UWP 的自然迁移目标。WinUI 3 使用相同的 XAML API 接口,但命名空间有所变化。
命名空间变更
主要迁移工作是将命名空间从 Windows.UI.* 更新为 Microsoft.UI.*:
| UWP 命名空间 | WinUI 3 命名空间 |
|---|---|
Windows.UI.Xaml | Microsoft.UI.Xaml |
Windows.UI.Xaml.Controls | Microsoft.UI.Xaml.Controls |
Windows.UI.Xaml.Media | Microsoft.UI.Xaml.Media |
Windows.UI.Composition | Microsoft.UI.Composition |
Windows.UI.Text | Microsoft.UI.Text |
保持不变: Windows.Storage、Windows.Networking、Windows.Security、Windows.ApplicationModel、Windows.Devices —— 这些 WinRT API 保持不变。
应用模型差异
| 关注点 | UWP | WinUI 3 |
|---|---|---|
| 应用生命周期 | CoreApplication + 挂起 | Windows App SDK AppInstance |
| 窗口管理 | Window.Current(单例) | 手动跟踪窗口引用 |
| 调度程序 | CoreDispatcher.RunAsync | DispatcherQueue.TryEnqueue |
| 后台任务 | 内置,需要包标识 | 使用 MSIX 时可用,不使用时受限 |
| 文件访问 | 通过清单授予广泛的访问权限 | 标准 Win32 文件访问 + StorageFile |
| Store API | Windows.Services.Store | 相同(仍然可用) |
迁移步骤
- 使用 Windows App SDK 模板创建新的 WinUI 3 项目
- 复制源文件并更新命名空间(将
Windows.UI.Xaml改为Microsoft.UI.Xaml) - 替换已弃用的 API(将
Window.Current改为手动跟踪,将CoreDispatcher改为DispatcherQueue) - 将 MSIX 清单从 UWP 格式更新为 Windows App SDK 格式
- 迁移功能——检查并更新功能声明
- 更新 NuGet 包至兼容 Windows App SDK 的版本
- 测试 Windows 集成——通知、后台任务和文件关联的行为可能有所不同
UWP .NET 9 预览路径: Microsoft 宣布了对 .NET 9 上 UWP 的预览支持。如果全面迁移到 WinUI 3 的成本过高,这条路径可让 UWP 应用使用现代 .NET 运行时功能,而无需迁移 UI 框架。
有关 WinUI 3 开发模式,请参见 [skill:dotnet-winui]。
UWP 到 Uno Platform
当需要让 UWP 代码库支持跨平台时。Uno Platform 实现了 WinUI XAML API 表面,因此是 UWP 最自然的跨平台迁移路径。
由于 Uno Platform 使用的 WinUI XAML API 表面与 UWP XAML 基于的 API 表面相同,因此与迁移到其他跨平台框架相比,大部分 UWP 代码只需较少修改即可迁移:
- XAML 文件通常只需少量修改即可使用(Uno 支持
x:Bind、x:Load等) - ViewModel 和服务可直接迁移(与平台无关的代码)
- Windows 专属 API需要为非 Windows 目标平台采用跨平台替代方案
关键注意事项
- 使用
Windows.*API 的平台专属代码需要通过条件编译或抽象处理,以支持非 Windows 目标平台 - WinRT API(传感器、地理定位、媒体捕获)需要使用 Uno 实现或平台专属替代方案
- MSIX 打包仅适用于 Windows;其他平台使用各自的原生分发机制
有关 Uno Platform 开发模式,请参阅 [skill:dotnet-uno-platform]。有关各目标平台的部署,请参阅 [skill:dotnet-uno-targets]。
决策矩阵
使用此矩阵选择迁移路径。正确的选择取决于你的具体限制条件,不存在通用的“最佳”迁移目标。
按主要目标选择
| 目标 | 推荐路径 | 替代方案 |
|---|---|---|
| 风险最低、迁移最快 | WPF/WinForms 迁移到 .NET 8+ | -- |
| 现代 Windows UI | WPF 迁移到 WinUI 3 | 使用 Fluent 主题的 WPF .NET 9+ |
| 从 Windows 应用实现跨平台 | WPF 迁移到 Uno Platform | 在 Blazor 中重写关键路径 |
| 从 UWP 实现跨平台 | UWP 迁移到 Uno Platform | UWP 迁移到 WinUI 3(仅限 Windows) |
| UWP 现代化(仅限 Windows) | UWP 迁移到 WinUI 3 | UWP on .NET 9 preview |
| 旧版 WinForms 现代化 | WinForms 迁移到 .NET 8+ | 在 Blazor 或 WPF 中逐步重写 |
按限制条件选择
| 限制条件 | 最佳路径 | 原因 |
|---|---|---|
| 预算/时间有限 | 迁移到 .NET 8+(相同框架) | 工作量最低,代码库不变 |
| 必须仅支持 Windows | WinUI 3 或 WPF .NET 8+ | WinUI 适用于现代 UI;WPF 改动最少 |
| 必须支持跨平台 | Uno Platform | 借助 WinUI XAML API 表面实现最广泛的平台覆盖 |
| 已有大型 WPF 代码库 | 先迁移到 WPF .NET 8+,然后再评估 | 在重写 UI 前,先稳定运行于现代运行时 |
| 已有 UWP 代码库 | WinUI 3(Windows)或 Uno(跨平台) | 与现有代码最接近的 API 表面 |
| 团队具备 Web 技能 | Blazor(重写) | 将 Web 专业知识用于桌面/移动端 |
分阶段迁移策略
对于大型应用,分阶段迁移可以降低风险:
- 阶段 1:运行时迁移——从 .NET Framework 迁移到 .NET 8+(UI 框架不变)。验证兼容性、提升性能,并支持使用现代 NuGet 包。
- 阶段 2:模式现代化——采用 Host 构建器、DI、MVVM Toolkit 和现代 C#。请参阅 [skill:dotnet-wpf-modern] 或 [skill:dotnet-winforms-basics]。
- 阶段 3:UI 迁移(如有需要)——根据需求迁移到 WinUI 3、Uno Platform 或 Blazor。只有在阶段 1 和 2 稳定后才进行此步骤。
此方法可避免“大爆炸式”重写的风险,并在每个阶段逐步带来价值。
Agent 注意事项
- 在了解约束条件之前,不要将某个迁移目标推荐为“正确选择”。 始终列出各种选项及其权衡。预算、时间安排、团队专业能力和平台要求都会影响决策。
- 在建议更换 UI 框架之前,不要跳过 .NET 8+ 运行时迁移。 从 .NET Framework 迁移到 .NET 8+(使用相同的 UI 框架)应作为第 1 阶段。从 .NET Framework 将 WPF 迁移到 WinUI,要比从 .NET 8+ 迁移困难得多。
- 不要认为 WPF 已是“旧技术”而必须迁移离开。 .NET 8+ 上的 WPF 仍在积极维护,并持续推出新功能(.NET 9+ 中的 Fluent 主题、性能改进)。许多应用继续使用 WPF 就能很好地满足需求。
- 不要在未先运行
analyze的情况下使用dotnet-upgrade-assistant。 升级前务必先进行分析,以了解变更范围和潜在问题。 - 不要混淆 WPF XAML 与 WinUI XAML。 虽然概念相似,但 API 表面不同(
Window、DataContext与x:Bind、NavigationView模式)。迁移所需的不只是更改命名空间。 - 不要忘记,UWP 迁移到 WinUI 3 涉及应用模型变更,而不仅仅是更新命名空间。
Window.Current、CoreDispatcher、CoreApplicationAPI 都被不同模式取代。 - 不要为仅面向 Windows 的简单应用推荐 Uno Platform。 如果应用不需要跨平台,WinUI 3 或 WPF .NET 8+ 更简单,维护开销也更低。