mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-12 04:05:12 +00:00
Add WinUI 3 Expert (#865)
* WinUI agent * Improvements * Fixes * Add WinUI 3 unit testing guidance across agent, instructions, and skill - Expanded Testing section in agent with Unit Test App project setup, [TestMethod] vs [UITestMethod] attributes, and code examples - Added Testing rules to instructions file - Added Testing Migration section to migration guide skill with UWP-to-WinUI 3 test project mapping and updated checklist Reference: https://learn.microsoft.com/en-us/windows/apps/winui/winui3/testing/create-winui-unit-test-project Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update agents/winui3-expert.agent.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Copilot tweaks * Update agents/winui3-expert.agent.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update agents/winui3-expert.agent.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
277
skills/winui3-migration-guide/SKILL.md
Normal file
277
skills/winui3-migration-guide/SKILL.md
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
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
|
||||
|
||||
```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
|
||||
|
||||
```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
|
||||
|
||||
```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.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
|
||||
|
||||
```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 |
|
||||
|-------------|-------------------|
|
||||
| `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
|
||||
|
||||
```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 | `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)** |
|
||||
|
||||
```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 `[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
|
||||
|
||||
1. [ ] Replace all `Windows.UI.Xaml.*` using directives with `Microsoft.UI.Xaml.*`
|
||||
2. [ ] Replace `Windows.UI.Colors` with `Microsoft.UI.Colors`
|
||||
3. [ ] Replace `CoreDispatcher.RunAsync` with `DispatcherQueue.TryEnqueue`
|
||||
4. [ ] Replace `Window.Current` with `App.MainWindow` static property
|
||||
5. [ ] Add `XamlRoot` to all `ContentDialog` instances
|
||||
6. [ ] Initialize all pickers with `InitializeWithWindow.Initialize(picker, hwnd)`
|
||||
7. [ ] Replace `MessageDialog` with `ContentDialog`
|
||||
8. [ ] Replace `ApplicationView`/`CoreWindow` with `AppWindow`
|
||||
9. [ ] Replace `CoreApplicationViewTitleBar` with `AppWindowTitleBar`
|
||||
10. [ ] Replace all `GetForCurrentView()` calls with `AppWindow` equivalents
|
||||
11. [ ] Update interop for Share and Print managers
|
||||
12. [ ] Replace `IBackgroundTask` with `AppLifecycle` activation
|
||||
13. [ ] Update project file: TFM to `net10.0-windows10.0.22621.0`, add `<UseWinUI>true</UseWinUI>`
|
||||
14. [ ] Migrate unit tests to **Unit Test App (WinUI in Desktop)** project; use `[UITestMethod]` for XAML tests
|
||||
15. [ ] Test both packaged and unpackaged configurations
|
||||
Reference in New Issue
Block a user