dotnet-wpf-modern
Building WPF on .NET 8+. Host builder, MVVM Toolkit, Fluent theme, performance, modern C# patterns.
Install
mkdir -p .claude/skills/dotnet-wpf-modern && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17139" && unzip -o skill.zip -d .claude/skills/dotnet-wpf-modern && rm skill.zipInstalls to .claude/skills/dotnet-wpf-modern
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Building WPF on .NET 8+. Host builder, MVVM Toolkit, Fluent theme, performance, modern C# patterns.About this skill
dotnet-wpf-modern
WPF on .NET 8+: Host builder and dependency injection, MVVM with CommunityToolkit.Mvvm source generators, hardware-accelerated rendering improvements, modern C# patterns (records, primary constructors, pattern matching), Fluent theme (.NET 9+), system theme detection, and what changed from .NET Framework WPF.
Version assumptions: .NET 8.0+ baseline (current LTS). TFM net8.0-windows. .NET 9 features (Fluent theme) explicitly marked.
Scope boundary: This skill owns WPF on modern .NET patterns: Host builder, MVVM Toolkit, performance, modern C#, theming. Migration from .NET Framework to .NET 8+ is owned by [skill:dotnet-wpf-migration]. Desktop testing is owned by [skill:dotnet-ui-testing-core].
Out of scope: WPF .NET Framework patterns (legacy) -- this skill covers .NET 8+ only. Migration guidance -- see [skill:dotnet-wpf-migration]. Desktop testing -- see [skill:dotnet-ui-testing-core]. General Native AOT patterns -- see [skill:dotnet-native-aot]. UI framework selection -- see [skill:dotnet-ui-chooser].
Cross-references: [skill:dotnet-ui-testing-core] for desktop testing, [skill:dotnet-winui] for WinUI 3 patterns, [skill:dotnet-wpf-migration] for migration guidance, [skill:dotnet-native-aot] for general AOT, [skill:dotnet-ui-chooser] for framework selection, [skill:dotnet-accessibility] for accessibility patterns (AutomationProperties, AutomationPeer, UI Automation).
.NET 8+ Differences
WPF on .NET 8+ is a significant modernization from .NET Framework WPF. The project format, DI pattern, language features, and runtime behavior have all changed.
New Project Template
<!-- MyWpfApp.csproj (SDK-style) -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.*" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.*" />
</ItemGroup>
</Project>
Key differences from .NET Framework WPF:
- SDK-style
.csproj(nopackages.config, noAssemblyInfo.cs) - Nullable reference types enabled by default
- Implicit usings enabled
- NuGet
PackageReferenceformat (notpackages.config) - No
App.configfor DI -- use Host builder dotnet publishproduces a single deployment artifact- Side-by-side .NET installation (no machine-wide framework dependency)
Host Builder Pattern
Modern WPF apps use the generic host for dependency injection, configuration, and logging -- replacing the legacy ServiceLocator or manual DI approaches.
// App.xaml.cs
public partial class App : Application
{
private readonly IHost _host;
public App()
{
_host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: true);
})
.ConfigureServices((context, services) =>
{
// Services
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IProductService, ProductService>();
services.AddSingleton<ISettingsService, SettingsService>();
// HTTP client
services.AddHttpClient("api", client =>
{
client.BaseAddress = new Uri(
context.Configuration["ApiBaseUrl"] ?? "https://api.example.com");
});
// ViewModels
services.AddTransient<MainViewModel>();
services.AddTransient<ProductListViewModel>();
services.AddTransient<SettingsViewModel>();
// Windows and pages
services.AddSingleton<MainWindow>();
})
.Build();
}
protected override async void OnStartup(StartupEventArgs e)
{
await _host.StartAsync();
var mainWindow = _host.Services.GetRequiredService<MainWindow>();
mainWindow.Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await _host.StopAsync();
_host.Dispose();
base.OnExit(e);
}
public static T GetService<T>() where T : class
{
var app = (App)Application.Current;
return app._host.Services.GetRequiredService<T>();
}
}
MVVM Toolkit
CommunityToolkit.Mvvm (Microsoft MVVM Toolkit) is the recommended MVVM framework for modern WPF. It uses source generators to eliminate boilerplate.
// ViewModels/ProductListViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class ProductListViewModel : ObservableObject
{
private readonly IProductService _productService;
public ProductListViewModel(IProductService productService)
{
_productService = productService;
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SearchCommand))]
private string _searchTerm = "";
[ObservableProperty]
private ObservableCollection<Product> _products = [];
[ObservableProperty]
private bool _isLoading;
[RelayCommand]
private async Task LoadProductsAsync(CancellationToken ct)
{
IsLoading = true;
try
{
var items = await _productService.GetProductsAsync(ct);
Products = new ObservableCollection<Product>(items);
}
finally
{
IsLoading = false;
}
}
[RelayCommand(CanExecute = nameof(CanSearch))]
private async Task SearchAsync(CancellationToken ct)
{
var results = await _productService.SearchAsync(SearchTerm, ct);
Products = new ObservableCollection<Product>(results);
}
private bool CanSearch() => !string.IsNullOrWhiteSpace(SearchTerm);
}
XAML Binding with MVVM Toolkit
<Window x:Class="MyApp.Views.ProductListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:MyApp.ViewModels"
d:DataContext="{d:DesignInstance vm:ProductListViewModel}">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="16">
<TextBox Text="{Binding SearchTerm, UpdateSourceTrigger=PropertyChanged}"
Width="300" Margin="0,0,8,0" />
<Button Content="Search" Command="{Binding SearchCommand}" />
</StackPanel>
<ListBox ItemsSource="{Binding Products}" Margin="16">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="4">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="0,0,12,0" />
<TextBlock Text="{Binding Price, StringFormat='{}{0:C}'}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
Key source generator attributes:
[ObservableProperty]-- generates property withINotifyPropertyChangedfrom a backing field[RelayCommand]-- generatesICommandfrom a method (supports async, cancellation,CanExecute)[NotifyPropertyChangedFor]-- raisesPropertyChangedfor dependent properties[NotifyCanExecuteChangedFor]-- re-evaluates commandCanExecutewhen property changes
Performance
WPF on .NET 8+ delivers significant performance improvements over .NET Framework WPF.
Hardware-Accelerated Rendering
- DirectX 11 rendering path is the default on .NET 8+ (up from DirectX 9 on .NET Framework)
- GPU-accelerated text rendering improves text clarity and reduces CPU usage for text-heavy UIs
- Reduced GC pressure from runtime improvements (dynamic PGO, on-stack replacement)
Startup Time
- ReadyToRun (R2R) -- pre-compiled assemblies reduce JIT overhead at startup
- Tiered compilation -- fast startup with progressive optimization
- Trimming readiness --
.NET 8+WPF supports IL trimming for smaller deployment size
<!-- Enable trimming for smaller deployment -->
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
<!-- WPF apps need partial trim mode due to reflection usage -->
</PropertyGroup>
Trimming caveat: WPF relies heavily on XAML reflection for data binding and resource resolution. Use TrimMode=partial (not full) and test thoroughly. Compiled bindings and x:Type references are safer than string-based bindings for trimming.
Memory and GC
- Frozen object heap (.NET 8) -- static strings and singleton allocations placed on non-collected heap segments
- Dynamic PGO -- runtime profiles guide JIT optimizations for hot paths
- Reduced working set -- .NET 8 runtime uses less baseline memory than .NET Framework CLR
Expected Improvements
WPF on .NET 8 delivers measurable improvements over .NET Framework 4.8 across key metrics. Exact numbers depend on workload, hardware, and application complexity -- always benchmark your own scenarios:
- Cold startup -- significantly faster due to ReadyToRun, tiered compilation, and reduced framework initialization overhead
- UI virtualization -- improved rendering pipeline and GC reduce time for large ItemsControls (ListBox, DataGrid)
- GC pauses -- shorter and less frequent Gen2 collections from .NET 8 GC improvements (Dynamic PGO, frozen object heap, pinned object heap)
- Memory footprint -- lower baseline working set compared to .NET Framework CLR
Modern C#
.NET 8+ WPF projec
Content truncated.