Agent Skillsmicrosoft/PowerToys › wpf-to-winui3-migration

wpf-to-winui3-migration

GitHub

指导将PowerToys模块从WPF迁移至WinUI 3,涵盖XAML转换、命名空间替换、Dispatcher及资源文件处理等。

.github/skills/wpf-to-winui3-migration/SKILL.md microsoft/PowerToys

Trigger Scenarios

迁移 WPF 代码到 WinUI 3 转换 WPF XAML 到 WinUI

Install

npx skills add microsoft/PowerToys --skill wpf-to-winui3-migration -g -y
More Options

Non-standard path

npx skills add https://github.com/microsoft/PowerToys/tree/main/.github/skills/wpf-to-winui3-migration -g -y

Use without installing

npx skills use microsoft/PowerToys@wpf-to-winui3-migration

指定 Agent (Claude Code)

npx skills add microsoft/PowerToys --skill wpf-to-winui3-migration -a claude-code -g -y

安装 repo 全部 skill

npx skills add microsoft/PowerToys --all -g -y

预览 repo 内 skill

npx skills add microsoft/PowerToys --list

SKILL.md

Frontmatter
{
    "name": "wpf-to-winui3-migration",
    "license": "Complete terms in LICENSE.txt",
    "description": "Guide for migrating PowerToys modules from WPF to WinUI 3 (Windows App SDK). Use when asked to migrate WPF code, convert WPF XAML to WinUI, replace System.Windows namespaces with Microsoft.UI.Xaml, update Dispatcher to DispatcherQueue, replace DynamicResource with ThemeResource, migrate imaging APIs from System.Windows.Media.Imaging to Windows.Graphics.Imaging, convert WPF Window to WinUI Window, migrate .resx to .resw resources, migrate custom Observable\/RelayCommand to CommunityToolkit.Mvvm source generators, handle WPF-UI (Lepo) to WinUI native control migration, or fix installer\/build pipeline issues after migration. Keywords: WPF, WinUI, WinUI3, migration, porting, convert, namespace, XAML, Dispatcher, DispatcherQueue, imaging, BitmapImage, Window, ContentDialog, ThemeResource, DynamicResource, ResourceLoader, resw, resx, CommunityToolkit, ObservableProperty, WPF-UI, SizeToContent, AppWindow, SoftwareBitmap."
}

WPF to WinUI 3 Migration Skill

Migrate PowerToys modules from WPF (System.Windows.*) to WinUI 3 (Microsoft.UI.Xaml.* / Windows App SDK). Based on patterns validated in the ImageResizer module migration.

When to Use This Skill

  • Migrate a PowerToys module from WPF to WinUI 3
  • Convert WPF XAML files to WinUI 3 XAML
  • Replace System.Windows namespaces with Microsoft.UI.Xaml
  • Migrate Dispatcher usage to DispatcherQueue
  • Migrate custom Observable/RelayCommand to CommunityToolkit.Mvvm source generators
  • Replace WPF-UI (Lepo) controls with native WinUI 3 controls
  • Convert imaging code from System.Windows.Media.Imaging to Windows.Graphics.Imaging
  • Handle WPF Window vs WinUI Window differences (sizing, positioning, SizeToContent)
  • Migrate resource files from .resx to .resw with ResourceLoader
  • Fix installer/build pipeline issues after WinUI 3 migration
  • Update project files, NuGet packages, and signing config

Prerequisites

  • Visual Studio 2022 17.4+
  • Windows App SDK NuGet package (Microsoft.WindowsAppSDK)
  • .NET 8+ with net8.0-windows10.0.19041.0 TFM
  • Windows 10 1803+ (April 2018 Update or newer)

Migration Strategy

Phase-by-Phase Scope

Work on bounded problems, not the entire codebase at once. Each phase should compile before moving to the next.

  1. Project file — Update TFM, NuGet packages, set <UseWinUI>true</UseWinUI>
  2. Data models and business logic — No UI dependencies, migrate first
  3. MVVM framework — Replace custom Observable/RelayCommand with CommunityToolkit.Mvvm
  4. Resource strings — Migrate .resx.resw, introduce ResourceLoaderInstance
  5. Services and utilities — Replace System.Windows types, async-ify imaging code
  6. ViewModels — Update Dispatcher usage, binding patterns
  7. Views/Pages — Starting from leaf pages with fewest dependencies
  8. Main page / shell — Last, since it depends on everything
  9. App.xaml / startup code — Merge carefully (do NOT overwrite WinUI 3 boilerplate)
  10. Installer & build pipeline — Update WiX, signing, build events
  11. Tests — Adapt for WinUI 3 runtime, async patterns

Migration Contract: Prohibited Patterns

These rules capture human judgment and must be applied consistently across every file. Do NOT deviate.

Architecture prohibitions:

  • Do NOT overwrite App.xaml / App.xaml.cs — WinUI 3 has different lifecycle boilerplate. Merge resources and init code into the generated WinUI 3 App class.
  • Do NOT create Exe→WinExe ProjectReference — Extract shared code to a Library project. Causes phantom build artifacts.
  • Do NOT instantiate services directly — Use DI and CommunityToolkit.Mvvm patterns.
  • Do NOT create a Window subclass for every dialog or sub-page — use ContentDialog for in-app dialogs and Frame/Page navigation for sub-views. Separate Window classes are reserved for distinct top-level surfaces (e.g., FancyZones editor, OOBE).
  • Do NOT omit WindowsPackageType=None and WindowsAppSDKSelfContained=true — Both are mandatory in the csproj for every WinUI 3 module in PowerToys. Without them the app crashes at startup with COMException: ClassFactory cannot supply requested class because the WinUI 3 runtime DLLs are not found.
  • Do NOT default to a bare Window, and do NOT hand-write code-behind for windowing that XAML can express — For a top-level window in a WinUI 3 module, use WinUIEx.WindowEx or an existing PowerToys base derived from it (for example, TransparentWindow for transient overlays). WindowEx restores WPF-like Window members as XAML properties (MinWidth/MinHeight, Width/Height, IsResizable, IsMaximizable/IsMinimizable, IsTitleBarVisible, IsAlwaysOnTop, IsShownInSwitchers, WindowState, SystemBackdrop) plus helpers (CenterOnScreen(), PersistenceId), so windowing is declared in XAML instead of manual AppWindow/OverlappedPresenter code-behind. This is the established PowerToys convention across ImageResizer, PowerDisplay, Peek, AdvancedPaste, MeasureTool, ShortcutGuide, Settings, Hosts, FileLocksmith, QuickAccess, and other WinUI 3 modules. Only drop to raw AppWindow/presenter code for behavior WindowEx does not expose. See Threading and Window Management → WindowEx.

XAML prohibitions:

  • Do NOT use {DynamicResource} — Replace with {ThemeResource} (theme-reactive) or {StaticResource}.
  • Do NOT use {Binding} in Setter.Value — Not supported in WinUI 3. Use {StaticResource}.
  • Do NOT use {x:Static} — Replace with {x:Bind}, x:Uid, or code-behind.
  • Do NOT use {x:Type} — Not supported. Use x:DataType for DataTemplate, or code-behind.
  • Do NOT use clr-namespace: — Replace with using: in all xmlns declarations.
  • Do NOT use Style.Triggers / DataTrigger / EventTrigger — Replace with VisualStateManager.
  • Do NOT use MultiBinding — Replace with x:Bind function binding or computed ViewModel property.
  • Do NOT mechanically port WPF IValueConverter classes — Prefer control VisualStates, direct {x:Bind} Boolean-to-Visibility conversion, resources supplied by XamlControlsResources, or CommunityToolkit.WinUI.Converters. Reuse converter instances and invert them with ConverterParameter=True when supported; write a custom converter only for app-specific conversion logic. See Value Converter Decision Guide.
  • Do NOT use Visibility="Hidden" — WinUI only has Visible and Collapsed. Use Opacity="0" if layout must be preserved.
  • Do NOT use IsDefault / IsCancel — Use AccentButtonStyle for primary button; handle Enter/Escape in code-behind.
  • Do NOT omit BasedOn when overriding default styles — Without it, your style replaces the entire default. Always use BasedOn="{StaticResource DefaultButtonStyle}" etc.
  • Do NOT omit XamlControlsResources as first merged dictionary — It provides default Fluent styles. Without it, controls have no visual appearance.

Code-behind prohibitions:

  • Do NOT use Application.Current.Dispatcher — Store DispatcherQueue in a static field explicitly.
  • Do NOT use Window.Current — Not supported. Use a custom App.Window static property.
  • Do NOT put DataContext, Resources, or VisualStateManager on Window — WinUI 3 Window is NOT a DependencyObject. Use a root Page/UserControl/Grid.
  • Do NOT use tunneling/preview events (PreviewMouseDown, PreviewKeyDown) — WinUI has no tunneling. Use bubbling equivalents with Handled property or AddHandler(handledEventsToo: true).

Resource prohibitions:

  • Do NOT use Properties.Resources.MyString — Replace with ResourceLoaderInstance.ResourceLoader.GetString("MyString").
  • Do NOT initialize ResourceLoader-dependent values as static fields — Wrap in Lazy<T> or null-coalescing property.
  • Do NOT use pack:// URIs — Replace with ms-appx:/// scheme.

Quick Reference Tables

Namespace Mapping

WPF WinUI 3 Notes
System.Windows Microsoft.UI.Xaml Root namespace
System.Windows.Controls Microsoft.UI.Xaml.Controls Core controls
System.Windows.Controls.Primitives Microsoft.UI.Xaml.Controls.Primitives Low-level primitives
System.Windows.Media Microsoft.UI.Xaml.Media Brushes, transforms
System.Windows.Media.Animation Microsoft.UI.Xaml.Media.Animation Storyboard, animations
System.Windows.Media.Imaging Microsoft.UI.Xaml.Media.Imaging (UI) / Windows.Graphics.Imaging (processing) Split by purpose
System.Windows.Media.Media3D No equivalent Use Win2D or Composition APIs
System.Windows.Shapes Microsoft.UI.Xaml.Shapes Rectangle, Ellipse, Path
System.Windows.Input Microsoft.UI.Xaml.Input Pointer, keyboard, focus
System.Windows.Data Microsoft.UI.Xaml.Data Binding, IValueConverter
System.Windows.Documents Microsoft.UI.Xaml.Documents Limited — RichTextBlock + Paragraph
System.Windows.Markup Microsoft.UI.Xaml.Markup XAML parsing, markup extensions
System.Windows.Automation Microsoft.UI.Xaml.Automation Accessibility / UI Automation
System.Windows.Navigation No direct equivalent Use Frame.Navigate()
System.Windows.Threading Microsoft.UI.Dispatching Dispatcher → DispatcherQueue
System.Windows.Interop WinRT.Interop / Microsoft.UI.Xaml.Hosting HWND interop

Control Replacements (No 1:1 Mapping)

These WPF controls have no direct counterpart and require a different control or third-party package:

WPF Control WinUI 3 Replacement Notes
DataGrid WinUI.TableView Community library; the Toolkit DataGrid is no longer maintained. Legacy code may still pin v7 CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2
Ribbon CommandBar / NavigationView, or Toolkit Labs Ribbon No first-party Ribbon in WinUI; Labs component is experimental/partial
Menu / MenuItem MenuBar / MenuBarItem / MenuFlyout MenuBar for classic menu, MenuFlyout for context
ContextMenu MenuFlyout Assign to ContextFlyout property
ToolBar / ToolBarTray CommandBar + AppBarButton
StatusBar Custom Grid/StackPanel or InfoBar No StatusBar control
TabControl TabView or NavigationView (top mode) TabView for closeable tabs
DocumentViewer WebView2 Render PDFs/XPS inside WebView2
FlowDocument RichTextBlock Partial replacement only
RichTextBox RichEditBox Rich text editing
GroupBox Expander (built-in) or HeaderedContentControl (Toolkit) See Layout & Header Controls from CommunityToolkit.WinUI below
Label TextBlock WPF Label is a ContentControl; use TextBlock + AccessKey
TreeView TreeView (native) Available natively, but data binding model differs significantly
MessageBox ContentDialog Must set XamlRoot before ShowAsync()
MediaElement MediaPlayerElement Different API
AccessText Not available Use AccessKey property on target control

Layout & Header Controls from CommunityToolkit.WinUI

These WPF controls have no built-in WinUI 3 equivalent — install the corresponding CommunityToolkit package. The NuGet package id and the XAML namespace differ intentionally: package names end in .Primitives / .HeaderedControls, but the registered XAML namespace is the shorter CommunityToolkit.WinUI.Controls (confirmed in the official Microsoft Q&A).

WPF Control WinUI 3 Replacement NuGet Package XAML Namespace
WrapPanel WrapPanel CommunityToolkit.WinUI.Controls.Primitives using:CommunityToolkit.WinUI.Controls
UniformGrid UniformGrid CommunityToolkit.WinUI.Controls.Primitives using:CommunityToolkit.WinUI.Controls
DockPanel DockPanel CommunityToolkit.WinUI.Controls.Primitives using:CommunityToolkit.WinUI.Controls
GroupBox (alt.) HeaderedContentControl CommunityToolkit.WinUI.Controls.HeaderedControls using:CommunityToolkit.WinUI.Controls

No Equivalent — Requires Architectural Rework

These WPF features have no WinUI counterpart and require redesign, not find-and-replace:

WPF Feature WinUI 3 Replacement Strategy
Style.Triggers / DataTrigger VisualStateManager with StateTrigger — see XAML Migration
MultiBinding x:Bind function binding: {x:Bind local:Converters.Format(VM.A, VM.B), Mode=OneWay}
RoutedUICommand / CommandBinding ICommand / [RelayCommand] from CommunityToolkit.Mvvm. WinUI also has StandardUICommand / XamlUICommand for platform commands.
AdornerLayer / Adorner Depends on use case: TeachingTip/InfoBar (validation), Popup (overlays), PlaceholderText (watermarks), Canvas overlay (decorations)
Visibility.Hidden Opacity="0" with Visibility="Visible" (preserves layout space)
Window.Resources / Window.DataContext Move to root Grid.Resources / root Page/UserControl — WinUI Window is NOT a DependencyObject
Tunneling events (Preview*) Use bubbling equivalents + Handled property or AddHandler(handledEventsToo: true)

Critical API Replacements

WPF WinUI 3 Notes
Dispatcher.Invoke() DispatcherQueue.TryEnqueue() Different return type (bool), async by default
Dispatcher.CheckAccess() DispatcherQueue.HasThreadAccess Property vs method
Application.Current.Dispatcher Store DispatcherQueue in static field See Threading
Window.Current Custom App.Window static property Not supported in Windows App SDK
Application.Current.MainWindow Custom App.Window static property Must track manually
MessageBox.Show() ContentDialog Must set XamlRoot
System.Windows.Clipboard Windows.ApplicationModel.DataTransfer.Clipboard Different API surface
RoutedUICommand / CommandBinding ICommand / [RelayCommand] Remove CommandBinding; bind ICommand directly
Properties.Resources.MyString ResourceLoaderInstance.ResourceLoader.GetString("MyString") Lazy-init pattern
DynamicResource ThemeResource Theme-reactive only
clr-namespace: using: XAML namespace prefix
{x:Static props:Resources.Key} x:Uid or ResourceLoader.GetString() .resx → .resw
DataType="{x:Type m:Foo}" x:DataType="m:Foo" x:Type not supported
SizeToContent="Height" Custom SizeToContent() via AppWindow.Resize() See Windowing
Pack URI (pack://...) ms-appx:/// Resource URI scheme
Observable (custom base) ObservableObject + [ObservableProperty] CommunityToolkit.Mvvm
RelayCommand (custom) [RelayCommand] source generator CommunityToolkit.Mvvm
JpegBitmapEncoder BitmapEncoder.CreateAsync(JpegEncoderId, stream) Async, unified API
encoder.QualityLevel = 85 BitmapPropertySet { "ImageQuality", 0.85f } int 1-100 → float 0-1

Event Replacements (Mouse → Pointer)

WPF Event WinUI 3 Event Notes
MouseLeftButtonDown PointerPressed Check IsLeftButtonPressed on args
MouseLeftButtonUp PointerReleased Check pointer properties
MouseRightButtonDown RightTapped Or PointerPressed with right button check
MouseMove PointerMoved MouseEventArgsPointerRoutedEventArgs
MouseWheel PointerWheelChanged Different event args
MouseEnter / MouseLeave PointerEntered / PointerExited
MouseDoubleClick DoubleTapped Different event args
PreviewMouseDown PointerPressed No tunneling — use Handled or AddHandler
PreviewKeyDown KeyDown KeyEventArgsKeyRoutedEventArgs

Property Replacements

WPF WinUI 3 Context
Visibility.Hidden Visibility.Collapsed or Opacity="0" Use Opacity="0" to preserve layout
TextWrapping.WrapWithOverflow TextWrapping.Wrap WinUI doesn't distinguish
Focusable="True" IsTabStop="True" Different property name
ContextMenu= ContextFlyout= On any UIElement
MediaElement MediaPlayerElement Different API
SnapsToDevicePixels Not available WinUI handles pixel snapping internally

NuGet Package Migration

WPF WinUI 3 Notes
Microsoft.Xaml.Behaviors.Wpf Microsoft.Xaml.Behaviors.WinUI.Managed
WPF-UI (Lepo) Remove — use native WinUI 3 controls
CommunityToolkit.Mvvm CommunityToolkit.Mvvm (same)
Microsoft.Toolkit.Wpf.* CommunityToolkit.WinUI.*
(none) Microsoft.WindowsAppSDK Required
(none) Microsoft.Windows.SDK.BuildTools Required
(none) WinUIEx Recommended for top-level windows in WinUI 3 modules — add <PackageReference Include="WinUIEx" /> (the version is centrally managed), then use WindowEx or an existing PowerToys base derived from it. It exposes WPF-like window properties in XAML (size, min/max, resizable, title-bar visibility, backdrop, always-on-top) plus CenterOnScreen()/PersistenceId; prefer it over bare Window and manual AppWindow code-behind
(none) CommunityToolkit.WinUI.Converters Optional
(none) CommunityToolkit.WinUI.Controls.Primitives Optional — WrapPanel, UniformGrid, DockPanel, ConstrainedBox
(none) CommunityToolkit.WinUI.Controls.HeaderedControls Optional — HeaderedContentControl, HeaderedItemsControl, HeaderedTreeView
(none) CommunityToolkit.WinUI.Controls.SettingsControls Optional — SettingsCard, SettingsExpander
(none) CommunityToolkit.WinUI.Controls.Sizers Optional — GridSplitter
(none) CommunityToolkit.WinUI.UI.Controls.DataGrid Legacy v7 — only for migrating existing DataGrid code; prefer WinUI.TableView

XAML Syntax Changes

WPF WinUI 3 Notes
xmlns:local="clr-namespace:MyApp" xmlns:local="using:MyApp" CLR → using syntax
{DynamicResource Key} {ThemeResource Key} Re-evaluates on theme change
{StaticResource Key} {StaticResource Key} Same — resolved once at load
{x:Static Type.Member} {x:Bind} or code-behind
{x:Type local:MyType} Not supported Use x:DataType for DataTemplate
{x:Array} Not supported Create collections in code-behind
<Style.Triggers> / <DataTrigger> VisualStateManager See XAML Migration
{Binding} in Setter.Value Not supported — use StaticResource
Content="{x:Static p:Resources.Cancel}" x:Uid="Cancel" with .Content in .resw
sys:String / sys:Int32 / etc. x:String / x:Int32 / etc. XAML intrinsic types
<ui:FluentWindow> (WPF-UI) <Window> Native + ExtendsContentIntoTitleBar
<ui:NumberBox> / <ui:ProgressRing> (WPF-UI) Native <NumberBox> / <ProgressRing>
BasedOn="{StaticResource {x:Type ui:Button}}" BasedOn="{StaticResource DefaultButtonStyle}" Named style keys
IsDefault="True" / IsCancel="True" Style="{StaticResource AccentButtonStyle}" / KeyDown
<AccessText> Not available — use AccessKey property
<behaviors:Interaction.Triggers> Code-behind or WinUI behaviors
Window.Resources Root container's Resources (e.g. Grid.Resources) Window is not a DependencyObject

Binding: {Binding} vs {x:Bind}

Both work in WinUI 3. Prefer {x:Bind} for new/migrated code.

Feature {Binding} {x:Bind}
Default mode OneWay OneTime — add Mode=OneWay explicitly!
Default source DataContext Page/UserControl code-behind
Compile-time validation No Yes
Function binding No Yes (replaces MultiBinding)
Performance Reflection-based Compiled, no reflection
MultiBinding support No (not in WinUI) Use function binding

Detailed Reference Docs

Read only the section relevant to your current task:

Common Pitfalls (from ImageResizer migration)

Pitfall Solution
ContentDialog throws "does not have a XamlRoot" Set dialog.XamlRoot = this.Content.XamlRoot before ShowAsync()
FilePicker throws error in desktop app Call WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd)
Window.Dispatcher returns null Use Window.DispatcherQueue instead
Resources on Window element not found Move resources to root layout container (Grid.Resources)
VisualStateManager on Window fails Use UserControl or Page inside the Window
Satellite assembly installer errors (WIX0103) Remove .resources.dll refs from Resources.wxs; WinUI 3 uses .pri
Phantom .exe/.deps.json in root output dir Avoid Exe→WinExe ProjectReference; use Library project
ResourceLoader crash at static init Wrap in Lazy<T> or null-coalescing property — see Lazy Init
SizeToContent not available Implement manual content measurement + AppWindow.Resize() with DPI scaling
x:Bind default mode is OneTime Explicitly set Mode=OneWay or Mode=TwoWay
DynamicResource / x:Static not compiling Replace with ThemeResource / ResourceLoader or x:Uid
IValueConverter.Convert signature mismatch Last param: CultureInfostring (language tag)
Test project can't resolve WPF types Add <UseWPF>true</UseWPF> temporarily; remove after imaging migration
Pixel dimension type mismatch (int vs uint) WinRT uses uint for pixel sizes — add u suffix in test assertions
$(SolutionDir) empty in standalone project build Use $(MSBuildThisFileDirectory) with relative paths instead
JPEG quality value wrong after migration WPF: int 1-100; WinRT: float 0.0-1.0
MSIX packaging fails in PreBuildEvent Move to PostBuildEvent; artifacts not ready at PreBuild time
RC file icon path with forward slashes Use double-backslash escaping: ..\\ui\\Assets\\icon.ico
COMException: ClassFactory cannot supply requested class at startup Missing <WindowsPackageType>None</WindowsPackageType> and/or <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> in csproj. Without these, the app tries to locate the Windows App SDK framework package (not installed) instead of using bundled runtime DLLs. Both properties are mandatory for every WinUI 3 module in PowerToys.
CombinedGeometry not available in WinUI 3 WinUI 3 UIElement.Clip only accepts RectangleGeometry. For overlay hole effects (exclude region), use a Path element with GeometryGroup FillRule="EvenOdd" containing two RectangleGeometry children — the EvenOdd rule creates a transparent hole where geometries overlap.

Troubleshooting

Issue Solution
Build fails after namespace rename Check for lingering System.Windows usings; some types have no direct equivalent
Missing PresentationCore.dll at runtime Ensure ALL imaging code uses Windows.Graphics.Imaging, not System.Windows.Media.Imaging
DataContext not working on Window WinUI 3 Window is not a DependencyObject; use a root Page or UserControl
XAML designer not available WinUI 3 does not support XAML Designer; use Hot Reload instead
NuGet restore failures Run build-essentials.cmd after adding Microsoft.WindowsAppSDK package
Parallel.ForEach compilation error Migrate to Parallel.ForEachAsync for async imaging operations
Signing check fails on leaked artifacts Run generateAllFileComponents.ps1; verify only WinUI3Apps\\ paths in signing config
COMException / ClassFactory error at app launch Ensure csproj has <WindowsPackageType>None</WindowsPackageType> and <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>. These are required for all unpackaged WinUI 3 apps in PowerToys — without them the WinUI 3 COM runtime cannot be found.

Version History

  • ddc536c Current 2026-08-20 08:30

Same Skill Collection

.github/skills/release-note-generation/SKILL.md
.github/skills/winmd-api-search/SKILL.md
.github/skills/powertoys-verification/SKILL.md
.github/skills/ui-tests-local-vm/SKILL.md
.github/skills/ui-tests-migration/SKILL.md

Metadata

Files
0
Version
ddc536c
Hash
c921336a
Indexed
2026-08-20 08:30

- 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-25 00:26
浙ICP备14020137号-1 $방문자$