--- name: dotnet-wpf-modern description: Builds WPF on .NET 8+. Host builder, MVVM Toolkit, Fluent theme, performance, modern C# patterns. license: MIT user-invocable: false --- # 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 - WPF .NET 8+ project setup (SDK-style) - Host builder and dependency injection - MVVM with CommunityToolkit.Mvvm source generators - Fluent theme (.NET 9+) and system theme detection - Hardware-accelerated rendering improvements - Modern C# patterns (records, primary constructors, pattern matching) ## Out of scope - WPF .NET Framework patterns (legacy) - 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 ```xml WinExe net8.0-windows true enable enable ``` **Key differences from .NET Framework WPF:** - SDK-style `.csproj` (no `packages.config`, no `AssemblyInfo.cs`) - Nullable reference types enabled by default - Implicit usings enabled - NuGet `PackageReference` format (not `packages.config`) - No `App.config` for DI -- use Host builder - `dotnet publish` produces 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. ```csharp // 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(); services.AddSingleton(); services.AddSingleton(); // HTTP client services.AddHttpClient("api", client => { client.BaseAddress = new Uri( context.Configuration["ApiBaseUrl"] ?? "https://api.example.com"); }); // ViewModels services.AddTransient(); services.AddTransient(); services.AddTransient(); // Windows and pages services.AddSingleton(); }) .Build(); } protected override async void OnStartup(StartupEventArgs e) { await _host.StartAsync(); var mainWindow = _host.Services.GetRequiredService(); mainWindow.Show(); base.OnStartup(e); } protected override async void OnExit(ExitEventArgs e) { await _host.StopAsync(); _host.Dispose(); base.OnExit(e); } public static T GetService() where T : class { var app = (App)Application.Current; return app._host.Services.GetRequiredService(); } } ``` --- ## MVVM Toolkit CommunityToolkit.Mvvm (Microsoft MVVM Toolkit) is the recommended MVVM framework for modern WPF. It uses source generators to eliminate boilerplate. ```csharp // 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 _products = []; [ObservableProperty] private bool _isLoading; [RelayCommand] private async Task LoadProductsAsync(CancellationToken ct) { IsLoading = true; try { var items = await _productService.GetProductsAsync(ct); Products = new ObservableCollection(items); } finally { IsLoading = false; } } [RelayCommand(CanExecute = nameof(CanSearch))] private async Task SearchAsync(CancellationToken ct) { var results = await _productService.SearchAsync(SearchTerm, ct); Products = new ObservableCollection(results); } private bool CanSearch() => !string.IsNullOrWhiteSpace(SearchTerm); } ``` ### XAML Binding with MVVM Toolkit ```xml