<!-- 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>
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