$npx -y skills add microsoft/win-dev-skills --skill winui-wpf-migrationMigrate WPF applications to WinUI 3 — namespace replacement (System.Windows → Microsoft.UI.Xaml), control mapping (DataGrid→ListView, WrapPanel→ItemsRepeater, TabControl→TabView), threading (Dispatcher→DispatcherQueue), imaging (System.Drawing→BitmapImage), MVVM conversion to Com
| 1 | ### Migration Process |
| 2 | |
| 3 | #### Step 1: Audit the WPF Source |
| 4 | Before writing code, inventory WPF-specific APIs: |
| 5 | ```powershell |
| 6 | # Find all WPF namespace usage |
| 7 | Select-String -Path (Get-ChildItem -Recurse -Filter "*.cs" | Where-Object { $_.FullName -notlike "*\obj\*" }) -Pattern "System\.Windows\." | Select-Object -Property Filename, LineNumber, Line |
| 8 | ``` |
| 9 | List: WPF controls used, custom MVVM framework, imaging APIs, threading patterns, Win32 interop. |
| 10 | |
| 11 | #### Step 2: Create WinUI 3 Project and Align Namespaces |
| 12 | ```powershell |
| 13 | dotnet new winui-mvvm -n <AppName> |
| 14 | ``` |
| 15 | Immediately set `<RootNamespace>` in `.csproj` to match the WPF namespace. Update `x:Class` in `App.xaml`, `MainWindow.xaml` and their code-behind files. Build to verify before porting any code. |
| 16 | |
| 17 | #### Step 3: Replace Namespaces |
| 18 | |
| 19 | | WPF | WinUI 3 | |
| 20 | |-----|---------| |
| 21 | | `System.Windows` | `Microsoft.UI.Xaml` | |
| 22 | | `System.Windows.Controls` | `Microsoft.UI.Xaml.Controls` | |
| 23 | | `System.Windows.Media` | `Microsoft.UI.Xaml.Media` | |
| 24 | | `System.Windows.Input` | `Microsoft.UI.Xaml.Input` | |
| 25 | | `System.Windows.Data` | `Microsoft.UI.Xaml.Data` | |
| 26 | | `System.Windows.Threading.Dispatcher` | `Microsoft.UI.Dispatching.DispatcherQueue` | |
| 27 | | `PresentationCore` / `PresentationFramework` | Remove entirely | |
| 28 | |
| 29 | #### Step 4: Replace Controls |
| 30 | |
| 31 | | WPF Control | WinUI 3 Equivalent | |
| 32 | |------------|-------------------| |
| 33 | | `DataGrid` | `ListView` with Grid column headers | |
| 34 | | `WrapPanel` | `ItemsRepeater` + `UniformGridLayout` | |
| 35 | | `TabControl` | `TabView` | |
| 36 | | `StatusBar` | `Grid` row at bottom with `TextBlock` elements | |
| 37 | | `Menu` / `MenuItem` | `MenuBar` / `MenuBarItem` / `MenuFlyoutItem` | |
| 38 | | `ToolBar` | `CommandBar` | |
| 39 | | `Expander` (custom) | `Expander` (built-in) | |
| 40 | |
| 41 | #### Step 5: Replace Threading |
| 42 | ```csharp |
| 43 | // WPF |
| 44 | Application.Current.Dispatcher.Invoke(() => { /* UI work */ }); |
| 45 | |
| 46 | // WinUI 3 |
| 47 | dispatcherQueue.TryEnqueue(() => { /* UI work */ }); |
| 48 | ``` |
| 49 | Get via `DispatcherQueue.GetForCurrentThread()`. No `Application.Current.Dispatcher` in WinUI 3. |
| 50 | |
| 51 | #### Step 6: Replace Imaging |
| 52 | **Critical:** `PresentationCore.dll` and `System.Windows.Media.Imaging` crash the WinUI XAML compiler. This is an architectural incompatibility — no workaround exists. |
| 53 | - Remove ALL `System.Windows.Media.Imaging` references at migration start |
| 54 | - Replace with `Windows.Graphics.Imaging` (WinRT) or `Microsoft.UI.Xaml.Media.Imaging.BitmapImage` |
| 55 | - Do NOT add `<UseWPF>true</UseWPF>` — it silently corrupts the build |
| 56 | - If heavy imaging code exists, migrate it early (step 2, not step 7) |
| 57 | |
| 58 | #### Step 7: Replace MVVM Framework |
| 59 | Delete custom `ObservableObject`/`RelayCommand`/`DelegateCommand`. Use CommunityToolkit.Mvvm: |
| 60 | - `INotifyPropertyChanged` base → `ObservableObject` with `[ObservableProperty]` partial properties |
| 61 | - Custom `RelayCommand` → `[RelayCommand]` attribute |
| 62 | - `{Binding}` → `{x:Bind Mode=OneWay}` |
| 63 | - `DynamicResource` → `{ThemeResource}` |
| 64 | |
| 65 | #### Step 8: Replace Resources |
| 66 | - `.resx` → `.resw` (copy + rename to `Strings\en-us\`) |
| 67 | - `{x:Static}` → `x:Uid` for localized strings |
| 68 | - `Properties.Resources.Key` → `ResourceLoader.GetString("Key")` |
| 69 | |
| 70 | ### Critical Rules |
| 71 | |
| 72 | - ❌ NEVER reference `PresentationCore`, `PresentationFramework`, or `System.Windows.Controls` assemblies |
| 73 | - ❌ NEVER add `<UseWPF>true</UseWPF>` or `<WindowsPackageType>None</WindowsPackageType>` |
| 74 | - ❌ NEVER delete `Package.appxmanifest` |
| 75 | - ❌ NEVER overwrite `App.xaml` / `App.xaml.cs` — merge WPF code into the WinUI 3 boilerplate |
| 76 | - ✅ Always use `winapp run` to launch — never run the .exe directly |
| 77 | - ✅ Break migration into file-level tasks — not one massive rewrite |
| 78 | |
| 79 | ### Post-Migration Validation |
| 80 | |
| 81 | ```powershell |
| 82 | # Check for remaining WPF references (should return nothing) |
| 83 | Select-String -Path (Get-ChildItem -Recurse -Filter "*.cs" | Where-Object { $_.FullName -notlike "*\obj\*" }) -Pattern "System\.Windows\." |
| 84 | |
| 85 | # Verify packaging preserved |
| 86 | Test-Path "Package.appxmanifest" # should be True |
| 87 | |
| 88 | # Build and run |
| 89 | .\BuildAndRun.ps1 |
| 90 | ``` |