Files
PowerToys/.github/skills/wpf-to-winui3-migration/references/xaml-migration.md
moooyo 7051b8939b [Skills] Add WPF to WinUI 3 migration agent skill (#46462)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
With this skills, we can easily enable AI to complete most of the tasks
involved in migrating from WPF to WinUI3.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46464
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:40:33 +00:00

11 KiB

XAML Migration Guide

Detailed reference for migrating XAML from WPF to WinUI 3, based on the ImageResizer migration.

XML Namespace Declaration Changes

Before (WPF)

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp"
        xmlns:m="clr-namespace:ImageResizer.Models"
        xmlns:p="clr-namespace:ImageResizer.Properties"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
        x:Class="MyApp.MainWindow">

After (WinUI 3)

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyApp"
        xmlns:m="using:ImageResizer.Models"
        xmlns:converters="using:ImageResizer.Converters"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        x:Class="MyApp.MainWindow">

Key Changes

WPF Syntax WinUI 3 Syntax Notes
clr-namespace:Foo using:Foo CLR namespace mapping
clr-namespace:Foo;assembly=Bar using:Foo Assembly qualification not needed
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" Remove entirely WPF-UI namespace no longer needed
xmlns:p="clr-namespace:...Properties" Remove No more .resx string bindings
sys:String (from mscorlib) x:String XAML intrinsic types
sys:Int32 x:Int32 XAML intrinsic types
sys:Boolean x:Boolean XAML intrinsic types
sys:Double x:Double XAML intrinsic types

Unsupported Markup Extensions

WPF Markup Extension WinUI 3 Alternative
{DynamicResource Key} {ThemeResource Key} (theme-reactive) or {StaticResource Key}
{x:Static Type.Member} {x:Bind} to a static property, or code-behind
{x:Type local:MyType} Not supported; use code-behind
{x:Array} Not supported; create collections in code-behind
{x:Code} Not supported

DynamicResource → ThemeResource

<!-- WPF -->
<TextBlock Foreground="{DynamicResource MyBrush}" />

<!-- WinUI 3 -->
<TextBlock Foreground="{ThemeResource MyBrush}" />

ThemeResource automatically updates when the app theme changes (Light/Dark/HighContrast). For truly dynamic non-theme resources, set values in code-behind or use data binding.

x:Static Resource Strings → x:Uid

This is the most pervasive XAML change. WPF used {x:Static} to bind to strongly-typed .resx resource strings. WinUI 3 uses x:Uid with .resw files.

WPF:

<Button Content="{x:Static p:Resources.Cancel}" />
<TextBlock Text="{x:Static p:Resources.Input_Header}" />

WinUI 3:

<Button x:Uid="Cancel" />
<TextBlock x:Uid="Input_Header" />

In Strings/en-us/Resources.resw:

<data name="Cancel.Content" xml:space="preserve">
    <value>Cancel</value>
</data>
<data name="Input_Header.Text" xml:space="preserve">
    <value>Select a size</value>
</data>

The x:Uid suffix (.Content, .Text, .Header, .PlaceholderText, etc.) matches the target property name.

DataType with x:Type → Remove

WPF:

<DataTemplate DataType="{x:Type m:ResizeSize}">

WinUI 3:

<DataTemplate x:DataType="m:ResizeSize">

WPF-UI (Lepo) Controls Removal

If the module uses the WPF-UI library, replace all Lepo controls with native WinUI 3 equivalents.

Window

<!-- WPF (WPF-UI) -->
<ui:FluentWindow
    ExtendsContentIntoTitleBar="True"
    WindowStartupLocation="CenterScreen">
    <ui:TitleBar Title="Image Resizer" />
    ...
</ui:FluentWindow>

<!-- WinUI 3 (native) -->
<Window>
    <!-- Title bar managed via code-behind: this.ExtendsContentIntoTitleBar = true; -->
    ...
</Window>

App.xaml Resources

<!-- WPF (WPF-UI) -->
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ui:ThemesDictionary Theme="Dark" />
            <ui:ControlsDictionary />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

<!-- WinUI 3 (native) -->
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Common Control Replacements

<!-- WPF-UI NumberBox -->
<ui:NumberBox Value="{Binding Width}" />
<!-- WinUI 3 -->
<NumberBox Value="{x:Bind ViewModel.Width, Mode=TwoWay}" />

<!-- WPF-UI InfoBar -->
<ui:InfoBar Title="Warning" Message="..." IsOpen="True" Severity="Warning" />
<!-- WinUI 3 -->
<InfoBar Title="Warning" Message="..." IsOpen="True" Severity="Warning" />

<!-- WPF-UI ProgressRing -->
<ui:ProgressRing IsIndeterminate="True" />
<!-- WinUI 3 -->
<ProgressRing IsActive="True" />

<!-- WPF-UI SymbolIcon -->
<ui:SymbolIcon Symbol="Add" />
<!-- WinUI 3 -->
<SymbolIcon Symbol="Add" />

Button Patterns

<!-- WPF -->
<Button IsDefault="True" Content="OK" />
<Button IsCancel="True" Content="Cancel" />

<!-- WinUI 3 (no IsDefault/IsCancel) -->
<Button Style="{StaticResource AccentButtonStyle}" Content="OK" />
<Button Content="Cancel" />
<!-- Handle Enter/Escape keys in code-behind if needed -->

Style and Template Changes

Triggers → VisualStateManager

WPF Triggers, DataTriggers, and EventTriggers are not supported.

WPF:

<Style TargetType="Button">
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="LightBlue"/>
        </Trigger>
        <DataTrigger Binding="{Binding IsEnabled}" Value="False">
            <Setter Property="Opacity" Value="0.5"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

WinUI 3:

<Style TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="PointerOver">
                                <VisualState.Setters>
                                    <Setter Target="RootGrid.Background" Value="LightBlue"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <ContentPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

No Binding in Setter.Value

<!-- WPF (works) -->
<Setter Property="Foreground" Value="{Binding TextColor}"/>

<!-- WinUI 3 (does NOT work — use StaticResource) -->
<Setter Property="Foreground" Value="{StaticResource TextColorBrush}"/>

Visual State Name Changes

WPF WinUI 3
MouseOver PointerOver
Disabled Disabled
Pressed Pressed

Resource Dictionary Changes

Window.Resources → Grid.Resources

WinUI 3 Window is NOT a DependencyObject — no Window.Resources, DataContext, or VisualStateManager.

<!-- WPF -->
<Window>
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Red"/>
    </Window.Resources>
    <Grid>...</Grid>
</Window>

<!-- WinUI 3 -->
<Window>
    <Grid>
        <Grid.Resources>
            <SolidColorBrush x:Key="MyBrush" Color="Red"/>
        </Grid.Resources>
        ...
    </Grid>
</Window>

Theme Dictionaries

<ResourceDictionary>
    <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key="Light">
            <SolidColorBrush x:Key="MyBrush" Color="#FF000000"/>
        </ResourceDictionary>
        <ResourceDictionary x:Key="Dark">
            <SolidColorBrush x:Key="MyBrush" Color="#FFFFFFFF"/>
        </ResourceDictionary>
        <ResourceDictionary x:Key="HighContrast">
            <SolidColorBrush x:Key="MyBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
        </ResourceDictionary>
    </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

URI Scheme Changes

WPF WinUI 3
pack://application:,,,/MyAssembly;component/image.png ms-appx:///Assets/image.png
pack://application:,,,/image.png ms-appx:///image.png
Relative path ../image.png ms-appx:///image.png

Assets directory convention: Resources/Assets/<Module>/

Data Binding Changes

{Binding} vs {x:Bind}

Both are available. Prefer {x:Bind} for compile-time safety and performance.

Feature {Binding} {x:Bind}
Default mode OneWay OneTime (explicit Mode=OneWay required!)
Context DataContext Code-behind class
Resolution Runtime Compile-time
Performance Reflection-based Compiled
Function binding No Yes

WPF-Specific Binding Features to Remove

<!-- These WPF-only features must be removed or rewritten -->
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
<!-- WinUI 3: UpdateSourceTrigger not needed; TextBox uses PropertyChanged by default -->
<TextBox Text="{x:Bind ViewModel.Value, Mode=TwoWay}" />

{Binding RelativeSource={RelativeSource Self}, ...}
<!-- WinUI 3: Use x:Bind which binds to the page itself, or use ElementName -->

<ItemsControl ItemsSource="{Binding}" />
<!-- WinUI 3: Must specify explicit path -->
<ItemsControl ItemsSource="{x:Bind ViewModel.Items}" />

WPF-Only Window Properties to Remove

These properties exist on WPF Window but not WinUI 3:

<!-- Remove from XAML — handle in code-behind via AppWindow API -->
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
ExtendsContentIntoTitleBar="True"  <!-- Set in code-behind -->

XAML Control Property Changes

WPF Property WinUI 3 Property Notes
Focusable IsTabStop Different name
SnapsToDevicePixels Not available WinUI handles pixel snapping internally
UseLayoutRounding UseLayoutRounding Same
IsHitTestVisible IsHitTestVisible Same
TextBox.VerticalScrollBarVisibility ScrollViewer.VerticalScrollBarVisibility (attached) Attached property

XAML Formatting (XamlStyler)

After migration, run XamlStyler to normalize formatting:

  • Alphabetize xmlns declarations and element attributes
  • Add UTF-8 BOM to all XAML files
  • Normalize comment spacing: <!-- text --><!-- text -->

PowerToys command: .\.pipelines\applyXamlStyling.ps1 -Main