winui3-migration-guide
github/awesome-copilot
Map legacy UWP APIs to WinUI 3 equivalents with before/after code snippets and common pitfalls.
What is winui3-migration-guide?
Reference guide for migrating UWP applications to WinUI 3 and Windows App SDK. Covers namespace changes, threading, windowing, dialogs, pickers, background tasks, and the most frequent Copilot code generation mistakes to avoid.
- Maps UWP namespaces to WinUI 3 equivalents (Windows.UI.Xaml → Microsoft.UI.Xaml)
- Provides before/after code examples for CoreDispatcher → DispatcherQueue, CoreWindow → AppWindow, and MessageDialog → ContentDialog
- Explains ContentDialog XamlRoot requirement and file picker window handle initialization
- Covers background task migration to Windows App SDK AppLifecycle patterns
- Identifies GetForCurrentView() replacements and threading model differences
- Includes unit test project migration guidance for WinUI 3 test templates
How to install winui3-migration-guide
npx skills add https://github.com/github/awesome-copilot --skill winui3-migration-guideHow to use winui3-migration-guide
- 1.Identify the UWP API or pattern in your code (e.g., CoreDispatcher, MessageDialog, Window.Current)
- 2.Search the guide for the corresponding section (Namespace Changes, Threading, Dialogs, etc.)
- 3.Review the ❌ WRONG example to confirm your current pattern
- 4.Copy the ✅ CORRECT example and adapt it to your context
- 5.For dialogs, ensure XamlRoot is set; for pickers, initialize with window handle; for threading, use DispatcherQueue.TryEnqueue()
- 6.Run your code and verify no InvalidOperationException or missing API errors occur
Use cases
- Migrating an existing UWP desktop app to WinUI 3 and Windows App SDK
- Verifying that AI-generated code uses correct WinUI 3 APIs instead of deprecated UWP patterns
- Fixing ContentDialog InvalidOperationException errors caused by missing XamlRoot
- Converting CoreDispatcher async patterns to DispatcherQueue in WinUI 3
- Updating file/folder picker code to initialize with window handles
- Desktop app developers migrating from UWP to WinUI 3
- Teams using Copilot or Claude Code to generate Windows App SDK code
- QA engineers reviewing generated code for API correctness
- Developers unfamiliar with WinUI 3 API surface
winui3-migration-guide FAQ
In WinUI 3, ContentDialog requires XamlRoot to be set before showing. Set dialog.XamlRoot = this.Content.XamlRoot before calling ShowAsync().
Use DispatcherQueue.TryEnqueue() for normal priority or DispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, callback) for high priority. Note: WinUI 3 uses standard STA without ASTA reentrancy blocking.
Create a static property in your App class (e.g., public static Window MainWindow) and set it in OnLaunched(). Access it anywhere as App.MainWindow. Window.Current does not exist in WinUI 3.
File pickers require window handle initialization. Use WinRT.Interop.WindowNative.GetWindowHandle() and WinRT.Interop.InitializeWithWindow.Initialize() before calling PickSingleFileAsync().
No. Migrate to WinUI 3 test project templates. Use [UITestMethod] for any test that instantiates Microsoft.UI.Xaml types.
Full instructions (SKILL.md)
Source of truth, from github/awesome-copilot.
name: winui3-migration-guide description: 'UWP-to-WinUI 3 migration reference. Maps legacy UWP APIs to correct Windows App SDK equivalents with before/after code snippets. Covers namespace changes, threading (CoreDispatcher to DispatcherQueue), windowing (CoreWindow to AppWindow), dialogs, pickers, sharing, printing, background tasks, and the most common Copilot code generation mistakes.'
WinUI 3 Migration Guide
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.
Namespace Changes
All Windows.UI.Xaml.* namespaces move to Microsoft.UI.Xaml.*:
| UWP Namespace | WinUI 3 Namespace |
|---|---|
Windows.UI.Xaml | Microsoft.UI.Xaml |
Windows.UI.Xaml.Controls | Microsoft.UI.Xaml.Controls |
Windows.UI.Xaml.Media | Microsoft.UI.Xaml.Media |
Windows.UI.Xaml.Input | Microsoft.UI.Xaml.Input |
Windows.UI.Xaml.Data | Microsoft.UI.Xaml.Data |
Windows.UI.Xaml.Navigation | Microsoft.UI.Xaml.Navigation |
Windows.UI.Xaml.Shapes | Microsoft.UI.Xaml.Shapes |
Windows.UI.Composition | Microsoft.UI.Composition |
Windows.UI.Input | Microsoft.UI.Input |
Windows.UI.Colors | Microsoft.UI.Colors |
Windows.UI.Text | Microsoft.UI.Text |
Windows.UI.Core | Microsoft.UI.Dispatching (for dispatcher) |
Top 3 Most Common Copilot Mistakes
1. ContentDialog Without XamlRoot
// ❌ WRONG — Throws InvalidOperationException in WinUI 3
var dialog = new ContentDialog
{
Title = "Error",
Content = "Something went wrong.",
CloseButtonText = "OK"
};
await dialog.ShowAsync();
// ✅ 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
// ❌ WRONG — UWP API, not available in WinUI 3 desktop
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure?", "Confirm");
await dialog.ShowAsync();
// ✅ 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
// ❌ WRONG — CoreDispatcher does not exist in WinUI 3
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
StatusText.Text = "Done";
});
// ✅ CORRECT — Use DispatcherQueue
DispatcherQueue.TryEnqueue(() =>
{
StatusText.Text = "Done";
});
// With priority:
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, () =>
{
ProgressBar.Value = 100;
});
Windowing Migration
Window Reference
// ❌ WRONG — Window.Current does not exist in WinUI 3
var currentWindow = Window.Current;
// ✅ 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.MainWindow
Window Management
| UWP API | WinUI 3 API |
|---|---|
ApplicationView.TryResizeView() | AppWindow.Resize() |
AppWindow.TryCreateAsync() | AppWindow.Create() |
AppWindow.TryShowAsync() | AppWindow.Show() |
AppWindow.TryConsolidateAsync() | AppWindow.Destroy() |
AppWindow.RequestMoveXxx() | AppWindow.Move() |
AppWindow.GetPlacement() | AppWindow.Position property |
AppWindow.RequestPresentation() | AppWindow.SetPresenter() |
Title Bar
| UWP API | WinUI 3 API |
|---|---|
CoreApplicationViewTitleBar | AppWindowTitleBar |
CoreApplicationView.TitleBar.ExtendViewIntoTitleBar | AppWindow.TitleBar.ExtendsContentIntoTitleBar |
Dialogs and Pickers Migration
File/Folder Pickers
// ❌ WRONG — UWP style, no window handle
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();
// ✅ 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 |
|---|---|
CoreDispatcher.RunAsync(priority, callback) | DispatcherQueue.TryEnqueue(priority, callback) |
Dispatcher.HasThreadAccess | DispatcherQueue.HasThreadAccess |
CoreDispatcher.ProcessEvents() | No equivalent — restructure async code |
CoreWindow.GetForCurrentThread() | Not available — use DispatcherQueue.GetForCurrentThread() |
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.
Background Tasks Migration
// ❌ WRONG — UWP IBackgroundTask
public sealed class MyTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance) { }
}
// ✅ 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 | ApplicationData.Current.LocalSettings | JSON file in LocalApplicationData |
| Local file storage | ApplicationData.Current.LocalFolder | Environment.GetFolderPath(SpecialFolder.LocalApplicationData) |
GetForCurrentView() Replacements
All GetForCurrentView() patterns are unavailable in WinUI 3 desktop apps:
| UWP API | WinUI 3 Replacement |
|---|---|
UIViewSettings.GetForCurrentView() | Use AppWindow properties |
ApplicationView.GetForCurrentView() | AppWindow.GetFromWindowId(windowId) |
DisplayInformation.GetForCurrentView() | Win32 GetDpiForWindow() or XamlRoot.RasterizationScale |
CoreApplication.GetCurrentView() | Not available — track windows manually |
SystemNavigationManager.GetForCurrentView() | Handle back navigation in NavigationView directly |
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 |
[TestMethod] for all tests | [TestMethod] for logic, [UITestMethod] for XAML/UI tests |
| Class Library (Universal Windows) | Class Library (WinUI in Desktop) |
// ✅ 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 [UITestMethod] attribute tells the test runner to execute the test on the XAML UI thread, which is required for instantiating any Microsoft.UI.Xaml type.
Migration Checklist
- Replace all
Windows.UI.Xaml.*using directives withMicrosoft.UI.Xaml.* - Replace
Windows.UI.ColorswithMicrosoft.UI.Colors - Replace
CoreDispatcher.RunAsyncwithDispatcherQueue.TryEnqueue - Replace
Window.CurrentwithApp.MainWindowstatic property - Add
XamlRootto allContentDialoginstances - Initialize all pickers with
InitializeWithWindow.Initialize(picker, hwnd) - Replace
MessageDialogwithContentDialog - Replace
ApplicationView/CoreWindowwithAppWindow - Replace
CoreApplicationViewTitleBarwithAppWindowTitleBar - Replace all
GetForCurrentView()calls withAppWindowequivalents - Update interop for Share and Print managers
- Replace
IBackgroundTaskwithAppLifecycleactivation - Update project file: TFM to
net10.0-windows10.0.22621.0, add<UseWinUI>true</UseWinUI> - Migrate unit tests to Unit Test App (WinUI in Desktop) project; use
[UITestMethod]for XAML tests - Test both packaged and unpackaged configurations
Related skills
More from github/awesome-copilot and the wider catalog.

workiq-copilot
Query live Microsoft 365 emails, meetings, docs, and Teams data from the Copilot CLI via WorkIQ.

write-coding-standards-from-file
Generate coding standards documents by analyzing your project's existing code style and syntax patterns.

acquire-codebase-knowledge
Map, document, and onboard into existing codebases with structured analysis across stack, architecture, conventions, and concerns.

add-educational-comments
Add educational comments to code files to turn them into learning resources.

gh-stack
Manage stacked PRs and split multi-part work into reviewable branches with gh-stack.

postiz
Schedule and publish posts across 28+ social media and chat platforms from one CLI.