winui3-migration-guide
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWinUI 3 Migration Guide
WinUI 3 迁移指南
Use this skill when migrating UWP apps to WinUI 3 / Windows App SDK, or when verifying that generated code uses correct WinUI 3 APIs instead of legacy UWP patterns.
当你将 UWP 应用迁移到 WinUI 3 / Windows App SDK,或者验证生成的代码是否使用了正确的 WinUI 3 API 而非旧版 UWP 模式时,可以使用这份参考。
Namespace Changes
命名空间变更
All namespaces move to :
Windows.UI.Xaml.*Microsoft.UI.Xaml.*| UWP Namespace | WinUI 3 Namespace |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
所有 命名空间都迁移到 :
Windows.UI.Xaml.*Microsoft.UI.Xaml.*| UWP Namespace | WinUI 3 Namespace |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
Top 3 Most Common Copilot Mistakes
Copilot 最常见的 3 个错误
1. ContentDialog Without XamlRoot
1. ContentDialog 未设置 XamlRoot
csharp
// ❌ WRONG — Throws InvalidOperationException in WinUI 3
var dialog = new ContentDialog
{
Title = "Error",
Content = "Something went wrong.",
CloseButtonText = "OK"
};
await dialog.ShowAsync();csharp
// ✅ CORRECT — Set XamlRoot before showing
var dialog = new ContentDialog
{
Title = "Error",
Content = "Something went wrong.",
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot // Required in WinUI 3
};
await dialog.ShowAsync();csharp
// ❌ WRONG — Throws InvalidOperationException in WinUI 3
var dialog = new ContentDialog
{
Title = "Error",
Content = "Something went wrong.",
CloseButtonText = "OK"
};
await dialog.ShowAsync();csharp
// ✅ CORRECT — Set XamlRoot before showing
var dialog = new ContentDialog
{
Title = "Error",
Content = "Something went wrong.",
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot // Required in WinUI 3
};
await dialog.ShowAsync();2. MessageDialog Instead of ContentDialog
2. 使用 MessageDialog 而非 ContentDialog
csharp
// ❌ WRONG — UWP API, not available in WinUI 3 desktop
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure?", "Confirm");
await dialog.ShowAsync();csharp
// ✅ CORRECT — Use ContentDialog
var dialog = new ContentDialog
{
Title = "Confirm",
Content = "Are you sure?",
PrimaryButtonText = "Yes",
CloseButtonText = "No",
XamlRoot = this.Content.XamlRoot
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
// User confirmed
}csharp
// ❌ WRONG — UWP API, not available in WinUI 3 desktop
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure?", "Confirm");
await dialog.ShowAsync();csharp
// ✅ CORRECT — Use ContentDialog
var dialog = new ContentDialog
{
Title = "Confirm",
Content = "Are you sure?",
PrimaryButtonText = "Yes",
CloseButtonText = "No",
XamlRoot = this.Content.XamlRoot
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
// User confirmed
}3. CoreDispatcher Instead of DispatcherQueue
3. 使用 CoreDispatcher 而非 DispatcherQueue
csharp
// ❌ WRONG — CoreDispatcher does not exist in WinUI 3
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
StatusText.Text = "Done";
});csharp
// ✅ CORRECT — Use DispatcherQueue
DispatcherQueue.TryEnqueue(() =>
{
StatusText.Text = "Done";
});
// With priority:
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, () =>
{
ProgressBar.Value = 100;
});csharp
// ❌ WRONG — CoreDispatcher does not exist in WinUI 3
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
StatusText.Text = "Done";
});csharp
// ✅ CORRECT — Use DispatcherQueue
DispatcherQueue.TryEnqueue(() =>
{
StatusText.Text = "Done";
});
// With priority:
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, () =>
{
ProgressBar.Value = 100;
});Windowing Migration
窗口管理迁移
Window Reference
窗口引用
csharp
// ❌ WRONG — Window.Current does not exist in WinUI 3
var currentWindow = Window.Current;csharp
// ✅ CORRECT — Use a static property in App
public partial class App : Application
{
public static Window MainWindow { get; private set; }
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
MainWindow = new MainWindow();
MainWindow.Activate();
}
}
// Access anywhere: App.MainWindowcsharp
// ❌ WRONG — Window.Current does not exist in WinUI 3
var currentWindow = Window.Current;csharp
// ✅ CORRECT — Use a static property in App
public partial class App : Application
{
public static Window MainWindow { get; private set; }
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
MainWindow = new MainWindow();
MainWindow.Activate();
}
}
// Access anywhere: App.MainWindowWindow Management
窗口管理API对应
| UWP API | WinUI 3 API |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| UWP API | WinUI 3 API |
|---|---|
| |
| |
| |
| |
| |
| |
| |
Title Bar
标题栏
| UWP API | WinUI 3 API |
|---|---|
| |
| |
| UWP API | WinUI 3 API |
|---|---|
| |
| |
Dialogs and Pickers Migration
对话框与选择器迁移
File/Folder Pickers
文件/文件夹选择器
csharp
// ❌ WRONG — UWP style, no window handle
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();csharp
// ✅ CORRECT — Initialize with window handle
var picker = new FileOpenPicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();csharp
// ❌ WRONG — UWP style, no window handle
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();csharp
// ✅ CORRECT — Initialize with window handle
var picker = new FileOpenPicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();Threading Migration
线程处理迁移
| UWP Pattern | WinUI 3 Equivalent |
|---|---|
| |
| |
| No equivalent — restructure async code |
| Not available — use |
Key difference: UWP uses ASTA (Application STA) with built-in reentrancy blocking. WinUI 3 uses standard STA without this protection. Watch for reentrancy issues when async code pumps messages.
| UWP Pattern | WinUI 3 Equivalent |
|---|---|
| |
| |
| No equivalent — restructure async code |
| Not available — use |
关键差异:UWP 使用带有内置重入阻塞的 ASTA(Application STA)模型,WinUI 3 使用无该保护的标准 STA 模型。当异步代码泵送消息时,请注意重入问题。
Background Tasks Migration
后台任务迁移
csharp
// ❌ WRONG — UWP IBackgroundTask
public sealed class MyTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance) { }
}csharp
// ✅ CORRECT — Windows App SDK AppLifecycle
using Microsoft.Windows.AppLifecycle;
// Register for activation
var args = AppInstance.GetCurrent().GetActivatedEventArgs();
if (args.Kind == ExtendedActivationKind.AppNotification)
{
// Handle background activation
}csharp
// ❌ WRONG — UWP IBackgroundTask
public sealed class MyTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance) { }
}csharp
// ✅ CORRECT — Windows App SDK AppLifecycle
using Microsoft.Windows.AppLifecycle;
// Register for activation
var args = AppInstance.GetCurrent().GetActivatedEventArgs();
if (args.Kind == ExtendedActivationKind.AppNotification)
{
// Handle background activation
}App Settings Migration
应用设置迁移
| Scenario | Packaged App | Unpackaged App |
|---|---|---|
| Simple settings | | JSON file in |
| Local file storage | | |
| 场景 | 打包应用 | 未打包应用 |
|---|---|---|
| 简单设置 | | JSON file in |
| 本地文件存储 | | |
GetForCurrentView() Replacements
GetForCurrentView() 替换方案
All patterns are unavailable in WinUI 3 desktop apps:
GetForCurrentView()| UWP API | WinUI 3 Replacement |
|---|---|
| Use |
| |
| Win32 |
| Not available — track windows manually |
| Handle back navigation in |
WinUI 3 桌面应用中不支持所有 模式:
GetForCurrentView()| UWP API | WinUI 3 Replacement |
|---|---|
| Use |
| |
| Win32 |
| Not available — track windows manually |
| Handle back navigation in |
Testing Migration
测试迁移
UWP unit test projects do not work with WinUI 3. You must migrate to the WinUI 3 test project templates.
| UWP | WinUI 3 |
|---|---|
| Unit Test App (Universal Windows) | Unit Test App (WinUI in Desktop) |
| Standard MSTest project with UWP types | Must use WinUI test app for Xaml runtime |
| |
| Class Library (Universal Windows) | Class Library (WinUI in Desktop) |
csharp
// ✅ WinUI 3 unit test — use [UITestMethod] for any XAML interaction
[UITestMethod]
public void TestMyControl()
{
var control = new MyLibrary.MyUserControl();
Assert.AreEqual(expected, control.MyProperty);
}Key: The attribute tells the test runner to execute the test on the XAML UI thread, which is required for instantiating any type.
[UITestMethod]Microsoft.UI.XamlUWP 单元测试项目无法在 WinUI 3 中运行,你必须迁移到 WinUI 3 测试项目模板。
| UWP | WinUI 3 |
|---|---|
| Unit Test App (Universal Windows) | Unit Test App (WinUI in Desktop) |
| Standard MSTest project with UWP types | Must use WinUI test app for Xaml runtime |
| |
| Class Library (Universal Windows) | Class Library (WinUI in Desktop) |
csharp
// ✅ WinUI 3 unit test — use [UITestMethod] for any XAML interaction
[UITestMethod]
public void TestMyControl()
{
var control = new MyLibrary.MyUserControl();
Assert.AreEqual(expected, control.MyProperty);
}要点: 属性会告诉测试运行器在 XAML UI 线程上执行测试,实例化任何 类型都需要该配置。
[UITestMethod]Microsoft.UI.XamlMigration Checklist
迁移检查清单
- Replace all using directives with
Windows.UI.Xaml.*Microsoft.UI.Xaml.* - Replace with
Windows.UI.ColorsMicrosoft.UI.Colors - Replace with
CoreDispatcher.RunAsyncDispatcherQueue.TryEnqueue - Replace with
Window.Currentstatic propertyApp.MainWindow - Add to all
XamlRootinstancesContentDialog - Initialize all pickers with
InitializeWithWindow.Initialize(picker, hwnd) - Replace with
MessageDialogContentDialog - Replace /
ApplicationViewwithCoreWindowAppWindow - Replace with
CoreApplicationViewTitleBarAppWindowTitleBar - Replace all calls with
GetForCurrentView()equivalentsAppWindow - Update interop for Share and Print managers
- Replace with
IBackgroundTaskactivationAppLifecycle - Update project file: TFM to , add
net10.0-windows10.0.22621.0<UseWinUI>true</UseWinUI> - Migrate unit tests to Unit Test App (WinUI in Desktop) project; use for XAML tests
[UITestMethod] - Test both packaged and unpackaged configurations
- 将所有 using 指令替换为
Windows.UI.Xaml.*Microsoft.UI.Xaml.* - 将 替换为
Windows.UI.ColorsMicrosoft.UI.Colors - 将 替换为
CoreDispatcher.RunAsyncDispatcherQueue.TryEnqueue - 将 替换为
Window.Current静态属性App.MainWindow - 为所有 实例添加
ContentDialog配置XamlRoot - 使用 初始化所有选择器
InitializeWithWindow.Initialize(picker, hwnd) - 将 替换为
MessageDialogContentDialog - 将 /
ApplicationView替换为CoreWindowAppWindow - 将 替换为
CoreApplicationViewTitleBarAppWindowTitleBar - 将所有 调用替换为对应的
GetForCurrentView()实现AppWindow - 更新分享和打印管理器的互操作逻辑
- 将 替换为
IBackgroundTask激活逻辑AppLifecycle - 更新项目文件:将 TFM 设置为 ,添加
net10.0-windows10.0.22621.0配置<UseWinUI>true</UseWinUI> - 将单元测试迁移到 Unit Test App (WinUI in Desktop) 项目;针对 XAML 测试使用
[UITestMethod] - 同时测试打包和未打包两种配置