名称: dotnet-wpf-migration 描述: “迁移桌面应用程序。WPF/WinForms 到 .NET 8+,WPF 到 WinUI 或 Uno,UWP 到 WinUI。” 用户可调用: false
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 功能在适用时明确标记。
范围
- 迁移决策矩阵(当前框架到目标框架)
- WPF .NET Framework 到 .NET 8+ 迁移
- WPF 到 WinUI 3(仅限 Windows 现代化)
- WPF 到 Uno Platform(跨平台)
- WinForms .NET Framework 到 .NET 8+
- UWP 到 WinUI 3 和 UWP 到 Uno Platform
超出范围
- WPF .NET 8+ 开发模式 – 见 [技能:dotnet-wpf-modern]
- WinUI 3 开发模式 – 见 [技能:dotnet-winui]
- WinForms .NET 8+ 开发模式 – 见 [技能:dotnet-winforms-basics]
- Uno Platform 开发模式 – 见 [技能:dotnet-uno-platform]
- 框架选择决策树 – 见 [技能:dotnet-ui-chooser]
- 桌面测试 – 见 [技能:dotnet-ui-testing-core]
交叉参考: [技能:dotnet-wpf-modern] 用于 WPF .NET 8+ 模式,[技能:dotnet-winui] 用于 WinUI 3 模式,[技能:dotnet-winforms-basics] 用于 WinForms .NET 8+ 模式,[技能:dotnet-uno-platform] 用于 Uno Platform 模式,[技能:dotnet-ui-chooser] 用于框架选择,[技能: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 升级助手自动化大部分迁移:
# 安装升级助手
dotnet tool install -g upgrade-assistant
# 首先分析项目(非破坏性)
upgrade-assistant analyze MyWpfApp.sln
# 升级项目
upgrade-assistant upgrade MyWpfApp.sln
升级助手处理的内容:
- 从旧格式转换
.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 兼容性
大多数 WPF API 在 .NET Framework 和 .NET 8+ 之间是相同的。关键差异:
| 区域 | .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 兼容版本:
# 列出过时包
dotnet list package --outdated
# 更新包(一次一个以安全迁移)
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 工具包、现代 C#),请参阅 [技能: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 命名空间) |
相同概念,不同命名空间 |
Style 与 TargetType |
相同 | 工作方式相同 |
DataTemplate |
需要 x:DataType 用于 x:Bind |
编译绑定所需 |
ContextMenu |
MenuFlyout |
不同的控件类型 |
StatusBar |
无内置等效 | 使用自定义 CommandBar 或 InfoBar |
RibbonControl |
无内置等效 | 使用 NavigationView + CommandBar |
迁移策略
- 创建新的 WinUI 3 项目 与现有 WPF 项目并行
- 首先迁移共享逻辑 – 模型、服务、视图模型(如果使用 MVVM 工具包,它们在两者中都有效)
- 增量迁移视图 – 从简单页面开始,然后复杂页面
- 替换 WPF 特定控件 为 WinUI 等效(见上表)
- 更新数据绑定 从
{Binding}到{x:Bind}以获取编译时安全 - 测试 Windows 集成 功能(通知、生命周期、文件关联)
- 选择部署模型 – MSIX(应用身份)或未打包(xcopy)
有关 WinUI 3 模式和项目设置,请参阅 [技能:dotnet-winui]。
WPF 到 Uno Platform
当需要从 WPF 代码库获得跨平台覆盖时迁移。Uno Platform 使用 WinUI XAML API 表面,因此 WPF XAML 技能部分转移,但迁移涉及适应 WinUI XAML 模式。
何时此路径有意义
- 应用程序需要运行在多个平台(Windows、macOS、Linux、Web、移动)
- 团队有WPF/XAML 专长,可转移到 WinUI XAML 表面
- 愿意投资跨平台适应(平台特定行为、响应式布局)
何时考虑替代方案
- 如果仅限 Windows -> 迁移到 WinUI 3(更简单,无跨平台开销)
- 如果应用程序处于维护模式 -> 迁移到 WPF .NET 8+(最低努力)
- 如果团队有Web 技能 -> 考虑 Blazor 用于 Web 交付
迁移方法
从 WPF 到 Uno Platform 的迁移类似于 WPF 到 WinUI 3(因为 Uno 使用 WinUI API 表面),但带有额外的跨平台考虑:
- 创建 Uno Platform 项目 与所需目标平台
- 迁移视图模型和服务 – 这些是平台独立的
- 适应 XAML 到 WinUI 语法(与 WPF 到 WinUI 迁移相同更改)
- 处理平台特定功能 使用条件编译或 Uno 平台扩展
- 替换仅限 Windows API 为跨平台替代(文件对话框、通知等)
- 测试每个目标平台 – 渲染和行为可能在 Skia 和原生目标之间不同
Uno 扩展用于常见模式
Uno 扩展提供常见 WPF 模式的跨平台替换:
| WPF 模式 | Uno 扩展替换 |
|---|---|
| 自定义导航 | Uno.Extensions.Navigation |
| 手动 DI 设置 | Uno.Extensions.DependencyInjection(包装 MS DI) |
appsettings.json 配置 |
Uno.Extensions.Configuration |
| 手动 HTTP 客户端 | Uno.Extensions.Http(带 Refit 的键入客户端) |
| 手动序列化 | Uno.Extensions.Serialization |
有关 Uno Platform 开发模式,请参阅 [技能:dotnet-uno-platform]。有关每目标部署指导,请参阅 [技能:dotnet-uno-targets]。
WinForms .NET Framework 到 .NET 8+
类似于 WPF 迁移,但通常更简单,因为 WinForms 的项目结构较不复杂。
使用 dotnet-upgrade-assistant
# 先分析
upgrade-assistant analyze MyWinFormsApp.sln
# 升级
upgrade-assistant upgrade MyWinFormsApp.sln
升级助手处理的内容:
.csproj转换为 SDK 风格,带<UseWindowsForms>true</UseWindowsForms>- TFM 更改为
net8.0-windows packages.config到PackageReferenceAssemblyInfo.cs迁移到项目属性
需要手动工作的内容:
App.config/Settings.settings迁移My.Settings(VB.NET) 迁移到现代配置- 第三方控件库更新(一些 WinForms 控件可能缺乏 .NET 8 支持)
- Crystal Reports 或其他遗留报告工具(评估替代方案)
- COM 互操作调整
设计器兼容性
WinForms 设计器文件(.Designer.cs)通常迁移干净。Visual Studio WinForms 设计器对于 .NET 8+ 完全支持。
已知设计器问题:
- 自定义设计器序列化器可能需要更新
- 一些第三方控件可能在 .NET 8 设计器中不渲染
- DPI 无感知设计器模式在 .NET 9+ 可用(
<ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>)
迁移后现代化
迁移到 .NET 8+ 后,考虑增量采用现代模式:
- 添加依赖注入 通过 Host 构建器(见 [技能:dotnet-winforms-basics])
- 替换同步调用 为 async/await
- 启用高 DPI 与
PerMonitorV2模式 - 启用暗模式(在 .NET 9+ 中实验性)
有关 WinForms .NET 8+ 模式,请参阅 [技能:dotnet-winforms-basics]。
UWP 到 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 |
| 商店 API | Windows.Services.Store |
相同(仍然可用) |
迁移步骤
- 创建新的 WinUI 3 项目 使用 Windows App SDK 模板
- 复制源文件 并更新命名空间(
Windows.UI.Xaml到Microsoft.UI.Xaml) - 替换已弃用 API(
Window.Current到手动跟踪,CoreDispatcher到DispatcherQueue) - 更新 MSIX 清单 从 UWP 格式到 Windows App SDK 格式
- 迁移功能 – 审查并更新功能声明
- 更新 NuGet 包 到 Windows App SDK 兼容版本
- 测试 Windows 集成 – 通知、后台任务、文件关联可能表现不同
UWP .NET 9 预览路径: 微软宣布 UWP 支持在 .NET 9 作为预览。如果完整 WinUI 3 迁移成本太高,这允许 UWP 应用使用现代 .NET 运行时功能,而无需迁移 UI 框架。
有关 WinUI 3 开发模式,请参阅 [技能:dotnet-winui]。
UWP 到 Uno Platform
当需要从 UWP 代码库获得跨平台覆盖时。Uno Platform 实现 WinUI XAML API 表面,使其成为 UWP 最自然的跨平台迁移路径。
由于 Uno Platform 使用与 UWP XAML 相同的 WinUI XAML API 表面,大部分 UWP 代码以较少更改转移,比迁移到其他跨平台框架:
- XAML 文件 通常只需最小更改(Uno 支持
x:Bind,x:Load等) - 视图模型和服务 直接转移(平台独立代码)
- Windows 特定 API 需要跨平台替代用于非 Windows 目标
关键考虑
- 平台特定代码 使用
Windows.*API 需要条件编译或抽象用于非 Windows 目标 - WinRT API(传感器、地理定位、媒体捕获)需要 Uno 实现或平台特定替代
- MSIX 打包 仅限 Windows – 其他平台使用其原生分发机制
有关 Uno Platform 开发模式,请参阅 [技能:dotnet-uno-platform]。有关每目标部署,请参阅 [技能:dotnet-uno-targets]。
决策矩阵
使用此矩阵决定采取哪个迁移路径。正确选择取决于您的特定约束 – 没有通用的“最佳”迁移目标。
按主要目标
| 目标 | 推荐路径 | 替代方案 |
|---|---|---|
| 最低风险,最快迁移 | WPF/WinForms 到 .NET 8+ | – |
| 现代 Windows UI | WPF 到 WinUI 3 | WPF .NET 9+ 带 Fluent 主题 |
| 从 Windows 应用跨平台 | WPF 到 Uno Platform | 重写关键路径在 Blazor |
| 从 UWP 跨平台 | UWP 到 Uno Platform | UWP 到 WinUI 3(保持仅限 Windows) |
| UWP 现代化(仅限 Windows) | UWP 到 WinUI 3 | UWP 在 .NET 9 预览 |
| 遗留 WinForms 现代化 | WinForms 到 .NET 8+ | 在 Blazor 或 WPF 中逐步重写 |
按约束
| 约束 | 最佳路径 | 原理 |
|---|---|---|
| 有限预算/时间 | .NET 8+ 迁移(相同框架) | 最低努力,相同代码库 |
| 必须保持仅限 Windows | WinUI 3 或 WPF .NET 8+ | WinUI 用于现代 UI;WPF 用于最小更改 |
| 必须跨平台 | Uno Platform | 最广泛覆盖,带 WinUI XAML 表面 |
| 大型现有 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 工具包、现代 C#。见 [技能:dotnet-wpf-modern] 或 [技能:dotnet-winforms-basics]。
- 阶段 3:UI 迁移(如果需要) – 迁移到 WinUI 3、Uno Platform 或 Blazor 基于需求。仅在阶段 1 和 2 稳定后进行。
此方法避免“大爆炸”重写风险,并在每个阶段提供增量价值。
代理陷阱
- 不要推荐单一迁移目标作为“正确选择”,而不理解约束。 始终以权衡呈现选项。预算、时间表、团队专长和平台需求都影响决策。
- 不要在建议 UI 框架更改之前跳过 .NET 8+ 运行时迁移。 从 .NET Framework 移动到 .NET 8+(相同 UI 框架)应该是阶段 1。从 .NET Framework 的 WPF 到 WinUI 迁移比从 .NET 8+ 更难。
- 不要假设 WPF 是“遗留”且必须迁移。 WPF 在 .NET 8+ 上积极维护,带有新功能(.NET 9+ 中的 Fluent 主题、性能改进)。许多应用通过停留在 WPF 良好服务。
- 不要使用
dotnet-upgrade-assistant而不先运行analyze。 始终在升级前分析,以了解更改范围和潜在问题。 - 不要混淆 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+ 更简单,维护开销较低。