wpfui Documentation

WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls.

9,726 stars C# C# 52 files · ~69,485 tokens #csharp#dotnet#fluent#mica#theme#ui#windows-10#windows-11
RAW Doc

Technical Documentation: lepoco/wpfui

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

WPF UI

Created with ❤ in Poland by Leszek Pomianowski and wonderful open-source community.
WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly. Library changes the base elements like Page, ToggleButton or List, and also includes additional controls like Navigation, NumberBox, Dialog or Snackbar.

Deliver humanitarian aid directly to Ukraine
https://bank.gov.ua/en/about/humanitarian-aid-to-ukraine

Support

To ensure you receive the expert guidance you need, we offer a variety of support plans designed to meet the diverse needs of our community. Whether you are looking to modernize your WPF applications or need assistance with our other libraries, our tailored support solutions are here to help. From priority email support to 24/7 dedicated assistance, we provide flexible plans to suit your project requirements.

Take a look at the lepo.co support plans
Sponsor WPF UI on GitHub

Getting started

For a starter guide see our documentation.

WPF UI Gallery is a free application available in the Microsoft Store, with which you can test all functionalities.
https://apps.microsoft.com/store/detail/wpf-ui/9N9LKV8R9VGM?cid=windows-lp-hero

powershell
winget install 'WPF UI'

WPF UI is delivered via NuGet package manager. You can find the package here:
https://www.nuget.org/packages/wpf-ui/

Visual Studio
The plugin for Visual Studio 2022 let you easily create new projects using WPF UI.
https://marketplace.visualstudio.com/items?itemName=lepo.wpf-ui

Screenshots

Works with Visual Studio Designer

Custom Tray icon and menu in pure WPF

Custom Windows 11 SnapLayout available for TitleBar

Development

If you want to propose a new functionality or submit a bugfix, create a Pull Request for the branch main.

How to use?

First, your application needs to load custom styles, add in the MyApp\App.xaml file:

xml
<Application
  ...
  xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ui:ThemesDictionary Theme="Light" />
        <ui:ControlsDictionary />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

If your application does not have MyApp\App.xaml file, use ApplicationThemeManager.Apply(frameworkElement) to apply/update the theme resource in the frameworkElement.

c#
public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        ApplicationThemeManager.Apply(this);
    }
}

Now you can create fantastic apps, e.g. with one button:

xml
<ui:FluentWindow
  ...
  xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
  <StackPanel>
      <ui:TitleBar Title="WPF UI"/>
      <ui:Card Margin="8">
          <ui:Button Content="Hello World" Icon="{ui:SymbolIcon Fluent24}" />
      </ui:Card>
  </StackPanel>
</ui:FluentWindow>

Microsoft Property

Design of the interface, choice of colors and the appearance of the controls were inspired by projects made by Microsoft for Windows 11.
The Wpf.Ui.Gallery app includes icons from Microsoft WinUI 3 Gallery app. They are used here as an example of creating tools for Microsoft systems.

Segoe Fluent Icons

WPF UI uses Fluent System Icons. Although this font was also created by Microsoft, it does not contain all the icons for Windows 11. If you need the missing icons, add Segoe Fluent Icons to your application.
According to the EULA of Segoe Fluent Icons we cannot ship a copy of it with this dll. Segoe Fluent Icons is installed by default on Windows 11, but if you want these icons in an application for Windows 10 and below, you must manually add the font to your application's resources.
https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font
https://docs.microsoft.com/en-us/windows/apps/design/downloads/#fonts

In the app dictionaries, you can add an alternate path to the font

xml
<FontFamily x:Key="SegoeFluentIcons">pack://application:,,,/;component/Fonts/#Segoe Fluent Icons</FontFamily>

Code of Conduct

This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.

License

WPF UI is free and open source software licensed under MIT License. You can use it in private and commercial projects.
Keep in mind that you must include a copy of the license in your project.


Overview (`docs/architecture/README.md`)

WPF UI - Architecture Documentation

Property Value
Project WPF UI (wpfui)
Version 4.2.0
Type Open-source WPF UI control library
License MIT
Language C# 14 / XAML
Platforms .NET 10/9/8 + .NET Framework 4.6.2/4.7.2/4.8.1

Overview

WPF UI is an open-source library that implements the Microsoft Fluent Design System for Windows Presentation Foundation (WPF) applications. It provides 77+ custom controls, a full theming system with light/dark/high-contrast modes, Win32 interop for modern window chrome effects (Mica, Acrylic, Tabbed backdrops), navigation services, and icon support through the Fluent System Icons font family.

The library enables thousands of developers to modernize legacy WPF applications with contemporary Windows 11 visual styles without migrating to WinUI 3 or MAUI.

Table of Contents

Architecture Views

Document Description
C4 Context (Level 1) System context diagram showing WPF UI in its environment -- consumer applications, Windows OS, distribution channels
Logical Architecture Module dependencies, layer diagram, core library internal component structure, detected patterns

Software Design Documents (SDD)

Document Description
CONSTITUTION.md Project constitution -- coding conventions, boundary system (ALWAYS DO / ASK FIRST / NEVER DO), rules files summary
IMPLEMENTATION-GUIDE.md Step-by-step guides for common tasks -- adding controls, services, gallery pages, Win32 interop, theming, testing
TESTING-SPEC.md Testing specification -- frameworks, templates, naming conventions, run commands
MODULE-INTERFACES.md Module interface contracts -- public vs internal API surface per module, dependency rules

Cross-Cutting Concerns

Document Description
Testing Test strategy, frameworks, naming conventions, coverage gaps
Theming and Appearance Theme system architecture, accent colors, backdrop effects, theme change flow
Win32 Interop Three-layer interop architecture, key APIs, handle validation, WndProc patterns
Navigation Navigation system lifecycle, page caching strategy, DI integration, transition animations

Architecture Decision Records (ADRs)

ADR Decision
ADR-001 Multi-target framework strategy (net10.0 through net462)
ADR-002 WPF control library architecture with Fluent Design
ADR-003 Win32 interop via CsWin32 source generator
ADR-004 Static singleton managers for theming (implicit)
ADR-005 Feature-folder organization for controls (implicit)

Other

Document Description
RECOMMENDATIONS.md Prioritized architectural improvement recommendations
UNCERTAINTIES.md Areas needing further investigation
Diagrams Index Index of all architecture diagrams
LikeC4 Diagrams Interactive C4 diagrams in LikeC4 DSL format

Quick Architecture Summary

WPF UI is organized as a set of layered NuGet packages built from a single solution:

  • Wpf.Ui.Abstractions -- Zero-dependency contract interfaces (INavigationViewPageProvider, INavigationAware, INavigableView<T>). Targets .NET and .NET Standard 2.0/2.1 for maximum portability.
  • Wpf.Ui -- Core library with 77+ controls, the theming engine, Win32 interop, services, converters, animations, and icon support. This is the primary NuGet package (WPF-UI).
  • Wpf.Ui.DependencyInjection -- Microsoft.Extensions.DependencyInjection integration for type-based page resolution.
  • Wpf.Ui.Tray -- System tray icon support via Shell32 P/Invoke.
  • Wpf.Ui.SyntaxHighlight -- Syntax-highlighted code display control.
  • Wpf.Ui.ToastNotifications -- Toast notification support (stub, not yet implemented).
  • Wpf.Ui.FlaUI -- FlaUI automation element wrappers for integration testing.
  • Wpf.Ui.FontMapper -- Build-time tool that generates SymbolRegular/SymbolFilled enums from Fluent System Icons font data.
  • Wpf.Ui.Gallery -- Comprehensive demo application showcasing all controls.
  • Wpf.Ui.Extension -- Visual Studio 2022 extension with project templates.

Key Architectural Concerns

  1. Control Architecture -- Folder-per-control pattern with paired .cs + .xaml files. Controls extend standard WPF base classes and implement capability interfaces (IAppearanceControl, IIconControl).
  2. Theming System -- Runtime resource dictionary swapping managed by ApplicationThemeManager. Accent colors derived from system settings via WinRT UISettings or DWM registry.
  3. Win32 Interop -- Three-layer architecture: CsWin32 source-generated P/Invoke, managed wrappers (UnsafeNativeMethods), and high-level utilities. Enables DWM backdrop effects, custom window chrome, and system theme detection.

Technology Stack

text
/* Detailed source-code truncated for AI context efficiency. */

Repository Structure

text
/* Detailed source-code truncated for AI context efficiency. */

Overview (`docs/templates/README.md`)

This folder contains the source code for website themes used by docfx.exe.


Overview (`src/Wpf.Ui.Abstractions/README.md`)

Wpf.Ui.Abstractions

Common interfaces and contracts for Wpf.Ui. Used to decouple core library features and enable extensibility.

Included types

csharp
Wpf.Ui.Abstractions.INavigationViewPageProvider
Wpf.Ui.Abstractions.NavigationViewPageProviderExtensions
Wpf.Ui.Abstractions.Controls.INavigableView
Wpf.Ui.Abstractions.Controls.INavigationAware
Wpf.Ui.Abstractions.Controls.NavigationAware

Documentation can be found at https://wpfui.lepo.co/. We also have a tutorial over there for newcomers.


Overview (`src/Wpf.Ui.DependencyInjection/README.md`)

Wpf.Ui.DependencyInjection

DI implementation for Wpf.Ui. Used to decouple core library features and enable extensibility.

Included types

csharp
Wpf.Ui.DependencyInjection.DependencyInjectionNavigationViewPageProvider

Documentation can be found at https://wpfui.lepo.co/. We also have a tutorial over there for newcomers.


Overview (`src/Wpf.Ui.FlaUI/README.md`)

Wpf.Ui.FlaUI

FlaUI automation helpers for Wpf.Ui. Provides test utilities for interacting with WPF controls in automated UI tests.

Included types

csharp
Wpf.Ui.FlaUI.AutoSuggestBox

Docs: https://wpfui.lepo.co/


Overview (`src/Wpf.Ui.SyntaxHighlight/README.md`)

Wpf.Ui.SyntaxHighlight

Syntax highlighting controls and utilities for Wpf.Ui. Enables code formatting and display in WPF applications.

Included types

csharp
Wpf.Ui.SyntaxHighlight.Highlighter
Wpf.Ui.SyntaxHighlight.Controls.CodeBlock
Wpf.Ui.SyntaxHighlight.Markup.SyntaxHighlightDictionary

Docs: https://wpfui.lepo.co/


Overview (`src/Wpf.Ui.ToastNotifications/README.md`)

Wpf.Ui.ToastNotifications

Toast notification support for Wpf.Ui. Provides a simple API for displaying toast messages in WPF applications.

Included types

csharp
Wpf.Ui.ToastNotifications.Toast

Docs: https://wpfui.lepo.co/


Overview (`src/Wpf.Ui.Tray/README.md`)

Wpf.Ui.Tray

System tray integration for Wpf.Ui. Provides services and controls for tray icons in WPF applications.

Included types

csharp
Wpf.Ui.Tray.NotifyIconService
Wpf.Ui.Tray.INotifyIconService
Wpf.Ui.Tray.Controls.NotifyIcon
Wpf.Ui.Tray.TrayManager
Wpf.Ui.Tray.TrayHandler
Wpf.Ui.Tray.TrayData
Wpf.Ui.Tray.RoutedNotifyIconEvent
Wpf.Ui.Tray.INotifyIcon
Wpf.Ui.Tray.Hicon
Wpf.Ui.Tray.NotifyIconEventHandler

Docs: https://wpfui.lepo.co/


Overview (`src/Wpf.Ui/README.md`)

WPF UI

WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly. Library changes the base elements like Page, ToggleButton or List, and also includes additional controls like Navigation, NumberBox, Dialog or Snackbar.

Documentation can be found at https://wpfui.lepo.co/. We also have a tutorial over there for newcomers.

WPF UI Gallery is a free application available in the Microsoft Store, with which you can test all functionalities.
https://apps.microsoft.com/store/detail/wpf-ui/9N9LKV8R9VGM?cid=windows-lp-hero

powershell
winget install 'WPF UI'

Visual Studio
The plugin for Visual Studio 2022 let you easily create new projects using WPF UI.
https://marketplace.visualstudio.com/items?itemName=lepo.wpf-ui

Support plans

To ensure you receive the expert guidance you need, we offer a variety of support plans designed to meet the diverse needs of our community. Whether you are looking to modernize your WPF applications or need assistance with our other libraries, our tailored support solutions are here to help. From priority email support to 24/7 dedicated assistance, we provide flexible plans to suit your project requirements.

Take a look at the lepo.co support plans

Help us keep working on this project

Support the development of WPF UI and other innovative projects by becoming a sponsor on GitHub! Your monthly or one-time contributions help us continue to deliver high-quality, open-source solutions that empower developers worldwide.

Sponsor WPF UI on GitHub


Overview (`docs/architecture/appendix/diagrams/README.md`)

Architecture Diagrams Index

This directory serves as an index of all architecture diagrams used throughout the WPF UI documentation.

Architecture Views

Context Diagram

NuGet Package Distribution Flowchart

  • Location: views/context.md
  • Type: Mermaid flowchart
  • Purpose: Shows the NuGet package distribution flow from GitHub Actions CD through NuGet.org to developer machines
  • Key Components: GitHub Actions CD, NuGet.org, Developer Machine

Logical Architecture

Module/Package Dependency Diagram

  • Location: views/logical-architecture.md
  • Type: Mermaid graph TD
  • Purpose: Shows dependencies between all 10 solution projects
  • Key Components: Abstractions, Core, DI, Tray, SyntaxHighlight, Toast, Gallery, FlaUI, FontMapper, Extension

Core Library Internal Structure (Layer Diagram)

  • Location: views/logical-architecture.md
  • Type: Mermaid graph TB
  • Purpose: Six-layer architecture of the core Wpf.Ui library
  • Layers: Controls, Appearance/Theming, Services, Infrastructure, Win32 Interop, Resources

Control Architecture Class Diagram

  • Location: views/logical-architecture.md
  • Type: Mermaid classDiagram
  • Purpose: Shows control inheritance and interface implementation patterns
  • Key Components: WPF base classes, capability interfaces (IAppearanceControl, IIconControl), concrete controls

Target Framework Matrix

  • Location: views/logical-architecture.md
  • Type: Mermaid gantt chart
  • Purpose: Visualizes TFM coverage across all modules
  • Key Components: All solution projects with their target frameworks

Service Registration & DI Integration

  • Location: views/logical-architecture.md
  • Type: Mermaid sequenceDiagram
  • Purpose: Shows Generic Host DI registration flow and runtime page resolution
  • Key Components: IHostBuilder, IServiceCollection, IServiceProvider, NavigationView, INavigationViewPageProvider

Control Lifecycle State Diagram

  • Location: views/logical-architecture.md
  • Type: Mermaid stateDiagram-v2
  • Purpose: Documents the complete lifecycle of WPF UI controls from construction through GC
  • States: Constructed, Loaded, Template Applied, Themed, Unloaded, GC

Cross-Cutting Concerns

Navigation System Lifecycle Sequence Diagram

  • Location: cross-cutting/navigation.md
  • Type: Mermaid sequenceDiagram
  • Purpose: Complete flow from NavigationService.Navigate() through page resolution, caching, INavigationAware callbacks, and transition animation
  • Key Components: Consumer Code, NavigationService, NavigationView, INavigationViewPageProvider, Page Cache, Frame, TransitionAnimationProvider

Page Cache Mode State Diagram

  • Location: cross-cutting/navigation.md
  • Type: Mermaid stateDiagram-v2
  • Purpose: Illustrates the three caching strategies (Disabled, Enabled, Required) and their decision logic
  • Key States: PageRequested, CheckCacheMode, CacheDisabled, CacheEnabled, CacheRequired, PageDisplayed

Theming and Appearance

Theme Change Flow Sequence Diagram

  • Location: cross-cutting/theming-and-appearance.md
  • Type: Mermaid sequence diagram
  • Purpose: Illustrates the complete flow of a theme change from OS event to UI update
  • Key Components: User, App, SystemThemeWatcher, WndProc, ApplicationThemeManager, ResourceDictionaryManager, UI

Theme System Architecture

  • Location: cross-cutting/theming-and-appearance.md
  • Type: Component description with code examples
  • Purpose: Documents the five core manager classes and their responsibilities
  • Key Components: ApplicationThemeManager, ApplicationAccentColorManager, SystemThemeWatcher, WindowBackgroundManager, ResourceDictionaryManager

Win32 Interop

Win32 Interop Component Diagram

  • Location: cross-cutting/win32-interop.md
  • Type: Mermaid graph TB with subgraphs
  • Purpose: Three-layer component diagram showing all Win32 interop participants from controls through managed wrappers to CsWin32-generated P/Invoke
  • Layers: High-Level Utilities & Controls, Managed Wrappers (Interop/), CsWin32 Source Generation

Three-Layer Architecture Diagram

  • Location: cross-cutting/win32-interop.md
  • Type: Mermaid graph diagram
  • Purpose: Shows the layered architecture for Win32 API access
  • Layers:
    • Layer 1: CsWin32 Generated Code (Windows.Win32 namespace)
    • Layer 2: Managed Wrappers (UnsafeNativeMethods, Custom PInvoke)
    • Layer 3: High-Level Utilities (Win32/Utilities, Appearance Managers)

WndProc Message Flow

  • Location: cross-cutting/win32-interop.md
  • Type: Code example with explanation
  • Purpose: Documents window message interception pattern used by SystemThemeWatcher and TitleBar
  • Key Messages: WM_DWMCOLORIZATIONCOLORCHANGED, WM_THEMECHANGED, WM_SYSCOLORCHANGE, WM_NCHITTEST

Architectural Decisions

ADR-002: Control Library Architecture

Control Folder Structure

Partial Class Decomposition

  • Location: decisions/ADR-002-control-library-architecture.md
  • Type: Directory structure with annotations
  • Purpose: Demonstrates how complex controls split into partial classes by concern
  • Example: NavigationView with 6 partial files (Base, Properties, Events, Navigation, TemplateParts, AttachedProperties)

ADR-003: Win32 Interop via CsWin32

Handle Validation Pattern Flowchart

  • Location: decisions/ADR-003-win32-interop-via-cswin32.md
  • Type: Code example with step-by-step comments
  • Purpose: Documents the mandatory three-step validation pattern for all Win32 calls
  • Steps:
    1. Check for zero handle
    2. Verify window exists (IsWindow)
    3. Perform operation with exception handling

ADR-005: Feature Folder Organization

Feature Folder Patterns

  • Location: decisions/ADR-005-feature-folder-controls.md
  • Type: Multiple directory tree visualizations
  • Purpose: Shows three organization patterns: Simple controls, Complex controls with partials, Controls with related types
  • Examples:
    • Simple: Badge/ (2 files)
    • Complex: NavigationView/ (7+ files)
    • With Related Types: NavigationView/ with NavigationViewItem, NavigationViewItemHeader, etc.

Module Interfaces

Module Dependency Graph (Public/Internal)

  • Location: MODULE-INTERFACES.md
  • Type: Mermaid graph TD with subgraphs
  • Purpose: Shows module dependencies with public/internal type counts per module
  • Key Components: All distributable and non-distributable modules with API surface annotations

Data Flow Diagrams

Theme Change Flow

Diagram: Sequence diagram showing theme change propagation

text
User → OS Settings → WndProc → SystemThemeWatcher → ApplicationThemeManager →
ResourceDictionaryManager → Application.Resources → UI Update

Win32 Call Stack

Diagram: Layer diagram showing Win32 interop call flow

text
High-Level Code → UnsafeNativeMethods (validation) → PInvoke (CsWin32) → Win32 API

Diagram: (Not yet created) Could be added to show:

text
NavigationService → NavigationView → PageProvider → Page Resolution →
Frame Navigation → INavigationAware Callbacks

Component Diagrams

Theming System Components

Components:

  • ApplicationThemeManager (theme selection)
  • ApplicationAccentColorManager (accent colors)
  • SystemThemeWatcher (OS sync)
  • WindowBackgroundManager (window appearance)
  • ResourceDictionaryManager (resource swapping)

Relationships:

  • SystemThemeWatcher → ApplicationThemeManager (triggers Apply)
  • ApplicationThemeManager → ResourceDictionaryManager (delegates swapping)
  • ApplicationAccentColorManager → ResourceDictionaryManager (updates dynamic resources)

Control Architecture Components

Components:

  • Control Class (.cs file)
  • Implicit Style (.xaml file)
  • Dependency Properties
  • Routed Events
  • Capability Interfaces (IAppearanceControl, IIconControl, IThemeControl)

Relationships:

  • Control Class → Dependency Properties (registers)
  • Control Class → Capability Interfaces (implements)
  • XAML Style → Control Class (targets via TargetType)

Future Diagram Additions

  1. Content Dialog Lifecycle - Async ShowAsync() flow with cancellation and result handling
  2. Icon System Hierarchy - IconElement inheritance tree (FontIcon, SymbolIcon, ImageIcon)
  3. Accent Color Derivation - How 20+ accent resources are computed from system accent
  4. Gallery App Architecture - MVVM structure with ViewModels, Views, Services, and Models

Recently Added (2026-02-10)

Diagram Guidelines

Mermaid Syntax

All diagrams use Mermaid for easy maintenance and version control:

Sequence Diagram:

mermaid
sequenceDiagram
    participant A
    participant B
    A->>B: Message
    B-->>A: Response

Graph Diagram:

mermaid
graph TD
    A[Component A] --> B[Component B]
    B --> C[Component C]

    style A fill:#e1f5e1
    style B fill:#fff4e1

Class Diagram:

mermaid
classDiagram
    class Button {
        +Icon IconElement
        +Appearance ControlAppearance
    }
    Button --|> IAppearanceControl
    Button --|> IIconControl

Diagram Best Practices

  1. Keep diagrams focused (single concern per diagram)
  2. Use consistent styling across related diagrams
  3. Include legend when colors/shapes have meaning
  4. Maintain diagram source in markdown (not separate image files)
  5. Update diagrams when architecture changes

Diagram Tools

  • Mermaid Live Editor - https://mermaid.live/
  • VS Code Extensions:
    • Markdown Preview Mermaid Support
    • Mermaid Editor

Generating PNG/SVG

For presentations or external documentation:

bash
# Using mmdc (Mermaid CLI)
npm install @mermaid-js/mermaid-cli
mmdc -i diagram.mmd -o diagram.png

Maintenance

Ownership

Architecture diagram maintenance is part of architecture documentation updates. When making architectural changes:

  1. Identify affected diagrams
  2. Update diagram source in markdown
  3. Verify rendering in preview
  4. Update this index if adding new diagrams

Review Checklist

  • Diagram accurately reflects current architecture
  • All components labeled clearly
  • Relationships shown with appropriate arrows
  • Color coding explained (if used)
  • Renders correctly in GitHub markdown preview
  • Index updated with new diagram location

Index (`docs/index.md`)

text
<h1 class="display-1">WPF UI</h1>
          A simple way to make your application written in WPF keep up with modern design trends. Library changes
              the base elements like Page, ToggleButton or List, and also includes additional controls like
              Navigation, NumberBox, Dialog or Snackbar.<a class="btn btn-outline-primary mr-05"
              href="/documentation/getting-started.html">Start tutorial</a><a class="btn btn-outline-light"
              href="/documentation">Read the docs</a>
      
  






  
    
      
        
          <h4>Check out the Gallery</h4>
          Check out all the controls and functionality with the help of the free **WPF UI Gallery ** app.
        
        
          <a href="https://apps.microsoft.com/store/detail/wpf-ui/9N9LKV8R9VGM?cid=windows-lp-hero" target="_blank" rel="noopener" class="btn btn-ms-store">
            
          </a>
        
      
    
  







  
    <h2 class="display-4">Fluent UI in your favorite environment</h2>
  



  
    
      
      **Open source**
      WPF UI is free and open source. You can check the source code or
        change it to your liking.
    
  


  
    
      
      **Easy to install**
      WPF UI is delivered via NuGet package manager. Just find it in
        the search and add to your project.
    
  


  
    
      
      **Compatible**
      Most styles and controls work right away in Visual Studio
        Designer.
    
  






    
        850KDownloads
    


    
        9KGitHub Stars
    


    
        900Forks

Support

To ensure you receive the expert guidance you need, we offer a variety of support plans designed to meet the diverse needs of our community. Whether you are looking to modernize your WPF applications or need assistance with our other libraries, our tailored support solutions are here to help. From priority email support to 24/7 dedicated assistance, we provide flexible plans to suit your project requirements.

text
<a class="btn btn-block btn-outline-primary mr-05" href="https://lepo.co/support">Take a look at the lepo.co support plans</a>


    <a class="btn btn-block btn-outline-light" href="https://github.com/sponsors/pomianowski">Sponsor WPF UI on GitHub</a>

Created with ❤ in Poland by lepo.co
A simple way to make your application written in WPF keep up with modern design trends. Library changes the base elements like Page, ToggleButton or List, and also includes additional controls like Navigation, NumberBox, Dialog or Snackbar.

Getting Started


Documentation Getting Started (`docs/documentation/getting-started.md`)

Getting started

Adding dictionaries

XAML, and hence WPF, operate on resource dictionaries. These are HTML-like files that describe the appearance and various aspects of the controls. WPF UI adds its own sets of these files to tell the application how the controls should look.

There should be a file called App.xaml in your new application. Add new dictionaries to it using WPF UI ControlsDictionary and ThemesDictionary classes:

xml
<Application
  ...
  xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ui:ThemesDictionary Theme="Dark" />
        <ui:ControlsDictionary />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

Notice that the ThemeDictionary lets you choose a color theme, Light or Dark.

The main window

There should be a MainWindow.xaml file in your newly created application. It contains the arrangement of the controls used and their parameters.

xml
<Window x:Class="WpfApp1.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>

Add the WPF UI library namespace to this window to tell the XAML compiler that you will be using controls from the library.

xml
<Window
  ...
  xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" />

Adding controls

To add a new control from the WPF UI library, just enter its class name, prefixing it with the ui: prefix:

xml
<Window x:Class="WpfApp1.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
      <ui:SymbolIcon Symbol="Fluent24"/>
    </Grid>
</Window>

Well...

That's it when it comes to the basics, information about individual controls can be found in documentation. Rules for building a WPF application can be found in the official Microsoft documentation. You can check out how to build MVVM applications here.

If you think this documentation needs improvement, please help improve it here.


Documentation Index (`docs/documentation/index.md`)

WPF UI Docs

WPF UI is a library built for Windows Presentation Foundation (WPF) and the C# language.
To be able to work with them comfortably, you will need:

[!NOTE]
Visual Studio 2022 and Visual Studio Code are two different programs. If you want to create WPF apps, it's possible to compile them in Visual Studio Code, however for comfortable work we recommend Visual Studio 2022 or JetBrains Rider.

Installation

You can install WPF UI, the library for the Windows Presentation Foundation framework, in several ways.

  • Directly specify the Wpf.Ui.dll file in your application's project file (.csproj).
  • Copy the library source code into your application codebase.
  • Use the NuGet package manager.

We recommend using the NuGet package manager, it allows you to easily install and update your application dependencies.
More information on how to install WPF UI using NuGet can be found here.

Extension for Visual Studio

Creators of WPF UI have prepared a special plugin that will automatically create a project based on WPF UI, Dependency Injection and MVVM, thanks to which you will quickly and easily start a new apps.

Learn more about the WPF UI plug-in for Visual Studio 2022

Getting started

Once you have chosen how to install WPF UI, you can move on to creating your first app, more on this in Getting Started.


Migration Index (`docs/migration/index.md`)

Migration Guides

Guides for migrating between major versions of Wpf.Ui.


Docfx.Json (`docs/docfx.json`)

{
"metadata": [
{
"src": [
{
"files": [
"Wpf.Ui/.csproj",
"Wpf.Ui.Tray/
.csproj",
"Wpf.Ui.Abstractions/.csproj",
"Wpf.Ui.DependencyInjection/
.csproj"
],
"src": "../src"
}
],
"dest": "api",
"includePrivateMembers": false,
"disableGitFeatures": false,
"disableDefaultFilter": false,
"noRestore": false,
"namespaceLayout": "flattened",
"memberLayout": "samePage",
"allowCompilationErrors": false
}
],
"build": {
"content": [
{
"files": [
"/*.{md,yml}"
],
"exclude": [
"_site/
",
"obj/",
"architecture/
"
]
}
],
"resource": [
{
"files": [
"/images/",
"codesnippet/",
"manifest.webmanifest",
"robots.txt"
],
"exclude": [
"_site/
",
"obj/**"
]
}
],
"xref": [],
"xrefService": [
"https://xref.docs.microsoft.com/query?uid={uid}"
],
"postProcessors": [
"ExtractSearchIndex"
],
"globalMetadata": {
"_appTitle": "WPF UI",
"_appName": "WPF UI",
"_appFaviconPath": "images/favicon.ico",
"_appLogoPath": "images/wpfui.png",
"_appFooter": "Made with <a href="https://dotnet.github.io/docfx\" rel="noreferrer">docfx, <a href="https://chat.openai.com/\" rel="noreferrer">ChatGPT | Copyright © 2026 <a href="https://lepo.co/\">lepo.co"
},
"dest": "_site",
"template": [
"default",
"templates/wpfui"
],
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"markdownEngineName": "markdig",
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false,
"disableGitFeatures": false
},
"sitemap": {
"baseUrl": "https://wpfui.lepo.co",
"priority": 0.1,
"changefreq": "monthly"
}
}


Toc.Yml (`docs/toc.yml`)

items:

  • name: Home
    href: /
  • name: Documentation
    href: /documentation/
    items:
    • name: Getting started
      href: documentation/getting-started.md
    • name: NuGet Package
      href: documentation/nuget.md
    • name: Visual Studio Extension
      href: documentation/extension.md
    • name: Themes
      href: documentation/themes.md
    • name: Icons
      href: documentation/icons.md
    • name: Theme Watcher
      href: documentation/system-theme-watcher.md
    • name: Theme Accent
      href: documentation/accent.md
    • name: Navigation
      href: documentation/navigation-view.md
    • name: Menu
      href: documentation/menu.md
    • name: Gallery
      href: documentation/gallery.md
    • name: Releases
      href: documentation/releases.md
  • name: API v4.0
    href: api/
  • name: Migration
    href: /migration/
    items:
    • name: Key changes in v4
      href: migration/v4-migration.md
    • name: Key changes in v3
      href: migration/v3-migration.md
    • name: Key changes in v2
      href: migration/v2-migration.md
  • name: Support plans
    href: https://lepo.co/support

Architecture CONSTITUTION (`docs/architecture/CONSTITUTION.md`)

WPF UI Project Constitution v4.2.0

Spec-Driven Development (SDD) Document
Single source of truth for coding conventions, boundary rules, and project-specific guidelines.
Intended audience: AI coding agents and human contributors.


Table of Contents

  1. Project Identity
  2. Coding Conventions
  3. Boundary System
  4. Rules Files Summary

1. Project Identity

Field Value
Name WPF UI (wpfui)
Version 4.2.0
Type Open-source WPF UI control library implementing Microsoft Fluent Design System
License MIT
Language C# 14 (LangVersion: preview) / XAML
Platforms .NET 10/9/8 (windows TFMs) + .NET Framework 4.6.2/4.7.2/4.8.1
NuGet Package ID WPF-UI
XAML Namespace http://schemas.lepo.co/wpfui/2022/xaml (prefix ui)
Repository https://github.com/lepoco/wpfui
Formatter CSharpier (110 char width, 4-space indent, no tabs)
Analyzers StyleCop, AsyncFixer, IDisposableAnalyzers, WpfAnalyzers (all enforced in build)
Test Frameworks XUnit v3, NSubstitute, AwesomeAssertions, FlaUI, Coverlet
Assembly Signing src/lepo.snk for release builds

Project Structure

Project Purpose TFMs
src/Wpf.Ui/ Core library (NuGet: WPF-UI) net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462
src/Wpf.Ui.Abstractions/ Interfaces and abstractions Includes netstandard2.0/2.1
src/Wpf.Ui.DependencyInjection/ Microsoft.Extensions.DependencyInjection integration Multi-target
src/Wpf.Ui.Tray/ System tray support Multi-target
src/Wpf.Ui.ToastNotifications/ Toast notification support Multi-target
src/Wpf.Ui.Gallery/ Demo/showcase application net10.0-windows10.0.26100.0
src/Wpf.Ui.Extension/ Visual Studio 2022 extension with project templates VS SDK
samples/ Sample apps (MVVM, DI, console hosting) Various

Core Library Layout (`src/Wpf.Ui/`)

Directory Contents
Controls/ Each control in its own subfolder with .cs + .xaml
Appearance/ ApplicationThemeManager, ApplicationAccentColorManager
Win32/ Raw P/Invoke declarations (native methods, structs, enums)
Interop/ Managed wrappers around Win32 APIs
Converters/ XAML value converters
AutomationPeers/ UI Automation/accessibility support
Resources/Fonts/ Embedded Fluent System Icons (Filled + Regular)
Root (*.cs) Service interfaces (I{Name}Service) and implementations ({Name}Service)

2. Coding Conventions

2.1 Namespaces

All C# files use file-scoped namespaces. All controls use the flat Wpf.Ui.Controls namespace regardless of subfolder depth, with a ReSharper suppression comment.

csharp
// ReSharper disable once CheckNamespace
namespace Wpf.Ui.Controls;

public class Button : System.Windows.Controls.Button, IAppearanceControl, IIconControl
{
    // ...
}

Services use the Wpf.Ui namespace:

csharp
namespace Wpf.Ui;

public class SnackbarService : ISnackbarService
{
    // ...
}

Gallery app follows folder-based namespaces:

csharp
namespace Wpf.Ui.Gallery.ViewModels.Pages.BasicInput;

public partial class AnchorViewModel : ViewModel
{
    // ...
}

2.2 Nullable Reference Types

Nullable is enabled globally via Directory.Build.props. Use is not null pattern matching over != null.

csharp
// Good
if (_presenter is null)
{
    throw new InvalidOperationException($"The SnackbarPresenter was never set");
}

_snackbar ??= new Snackbar(_presenter);

// Good - nullable property types
public IconElement? Icon
{
    get => (IconElement?)GetValue(IconProperty);
    set => SetValue(IconProperty, value);
}

2.3 Private Fields

Private fields use _camelCase (underscore prefix). This is enforced by .editorconfig naming rules.

csharp
private bool _valueUpdating;
private SnackbarPresenter? _presenter;
private Snackbar? _snackbar;

In Gallery ViewModels with CommunityToolkit.Mvvm, the same convention applies:

csharp
[ObservableProperty]
private bool _isAnchorEnabled = true;

2.4 File Headers

C# files -- MIT license header enforced as a build error via IDE0073:

csharp
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

XAML files -- XML comment block with MIT license and Microsoft credit where applicable:

xml

2.5 XML Documentation

Public APIs require `` and <example> tags. Examples show XAML usage with <code lang="xml"> and HTML-escaped angle brackets.

Control class documentation:

csharp
/// <summary>
/// Creates a hyperlink to web pages, files, email addresses, locations in the same page,
/// or anything else a URL can address.
/// </summary>
/// <example>
/// <code lang="xml">
/// &lt;ui:Anchor
///     NavigateUri="https://lepo.co/" /&gt;
/// </code>
/// </example>
public class Anchor : Wpf.Ui.Controls.HyperlinkButton { }

Control with multiple XAML examples:

csharp
/// <summary>
/// Inherited from the <see cref="System.Windows.Controls.Button"/>,
/// adding <see cref="SymbolRegular"/>.
/// </summary>
/// <example>
/// <code lang="xml">
/// &lt;ui:Button
///     Appearance="Primary"
///     Content="WPF UI button with font icon"
///     Icon="{ui:SymbolIcon Symbol=Fluent24}" /&gt;
/// </code>
/// <code lang="xml">
/// &lt;ui:Button
///     Appearance="Primary"
///     Content="WPF UI button with font icon"
///     Icon="{ui:FontIcon '&#x1F308;'}" /&gt;
/// </code>
/// </example>

C# code examples (e.g., for static managers):

csharp
/// <example>
/// <code lang="csharp">
/// ApplicationThemeManager.Apply(
///     ApplicationTheme.Light
/// );
/// </code>
/// </example>

DependencyProperty documentation uses a single-line ``:

csharp
/// <summary>Identifies the <see cref="Icon"/> dependency property.</summary>
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
    nameof(Icon),
    typeof(IconElement),
    typeof(Button),
    new PropertyMetadata(null, null, IconElement.Coerce)
);

Internal code: Avoid comments unless explaining Win32 interop/marshalling. Never add comments that restate what the code does.

2.6 Dependency Properties

Use DependencyProperty.Register with nameof(). Always include XML doc with the Identifies the ... dependency property pattern.

csharp
/// <summary>Identifies the <see cref="Icon"/> dependency property.</summary>
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
    nameof(Icon),
    typeof(IconElement),
    typeof(Button),
    new PropertyMetadata(null, null, IconElement.Coerce)
);

/// <summary>
/// Gets or sets displayed <see cref="IconElement"/>.
/// </summary>
[Bindable(true)]
[Category("Appearance")]
public IconElement? Icon
{
    get => (IconElement?)GetValue(IconProperty);
    set => SetValue(IconProperty, value);
}

For two-way binding with specific update triggers:

csharp
/// <summary>Identifies the <see cref="Value"/> dependency property.</summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
    nameof(Value),
    typeof(double?),
    typeof(NumberBox),
    new FrameworkPropertyMetadata(
        null,
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        OnValueChanged,
        null,
        false,
        UpdateSourceTrigger.LostFocus
    )
);

2.7 Control Authoring

Each control resides in its own subfolder under Controls/{Name}/ containing at minimum:

  • {Name}.cs -- C# control class
  • {Name}.xaml -- XAML ResourceDictionary with styles and templates

Complex controls are split into partial classes:

  • NavigationView.Base.cs -- Static constructor, instance constructor
  • NavigationView.Properties.cs -- DependencyProperty declarations
  • NavigationView.Events.cs -- RoutedEvent declarations
  • NavigationView.TemplateParts.cs -- Template part handling
  • NavigationView.Navigation.cs -- Navigation logic

Static constructor pattern (required for custom-styled controls):

csharp
static NavigationView()
{
    DefaultStyleKeyProperty.OverrideMetadata(
        typeof(NavigationView),
        new FrameworkPropertyMetadata(typeof(NavigationView))
    );
}

Template parts pattern:

csharp
[TemplatePart(Name = PART_ClearButton, Type = typeof(Button))]
[TemplatePart(Name = PART_InlineIncrementButton, Type = typeof(RepeatButton))]
[TemplatePart(Name = PART_InlineDecrementButton, Type = typeof(RepeatButton))]
public partial class NumberBox : Wpf.Ui.Controls.TextBox
{
    private const string PART_ClearButton = nameof(PART_ClearButton);
    private const string PART_InlineIncrementButton = nameof(PART_InlineIncrementButton);
    private const string PART_InlineDecrementButton = nameof(PART_InlineDecrementButton);
}

Base class selection:

Base Class Use For
System.Windows.Controls.ContentControl Card, Badge, InfoBar, Snackbar
System.Windows.Controls.Button Button
System.Windows.Controls.Control TitleBar, ProgressRing, NavigationView
System.Windows.Controls.Primitives.ToggleButton ToggleSwitch
System.Windows.Window FluentWindow

2.8 XAML Styles

Control styles require OverridesDefaultStyle=True and SnapsToDevicePixels=True. Use DynamicResource for theme-dependent values and StaticResource for constants.

xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:Wpf.Ui.Controls">

    
    <Thickness x:Key="ButtonPadding">11,5,11,6</Thickness>
    <Thickness x:Key="ButtonBorderThemeThickness">1</Thickness>

    <Style x:Key="DefaultRepeatButtonStyle" TargetType="{x:Type RepeatButton}">
        <Setter Property="FocusVisualStyle" Value="{DynamicResource DefaultControlFocusVisualStyle}" />
        <Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
        <Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
        <Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}" />
        <Setter Property="BorderThickness" Value="{StaticResource ButtonBorderThemeThickness}" />
        <Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="OverridesDefaultStyle" Value="True" />
    </Style>
</ResourceDictionary>

Resource key naming conventions:

Type Pattern Example
Brushes {Name}Brush ButtonForeground, TextFillColorPrimaryBrush
Colors {Name}Color AccentColor
Thickness {ControlName}{Property} ButtonPadding, CardBorderThemeThickness
Doubles {ControlName}{Property} ToggleButtonWidth

2.9 Service Pattern

Services follow the I{Name}Service / {Name}Service naming convention. Both interface and implementation live at the src/Wpf.Ui/ root level (NOT in a Services/ subfolder).

Interface (src/Wpf.Ui/ISnackbarService.cs):

csharp
namespace Wpf.Ui;

/// <summary>
/// Represents a contract with the service that provides global <see cref="Snackbar"/>.
/// </summary>
public interface ISnackbarService
{
    TimeSpan DefaultTimeOut { get; set; }
    void SetSnackbarPresenter(SnackbarPresenter contentPresenter);
    SnackbarPresenter? GetSnackbarPresenter();
    void Show(string title, string message, ControlAppearance appearance, IconElement? icon, TimeSpan timeout);
}

Implementation (src/Wpf.Ui/SnackbarService.cs):

csharp
namespace Wpf.Ui;

/// <summary>
/// A service that provides methods related to displaying the <see cref="Snackbar"/>.
/// </summary>
public class SnackbarService : ISnackbarService
{
    private SnackbarPresenter? _presenter;
    private Snackbar? _snackbar;

    /// <inheritdoc />
    public TimeSpan DefaultTimeOut { get; set; } = TimeSpan.FromSeconds(5);

    /// <inheritdoc />
    public void Show(
        string title,
        string message,
        ControlAppearance appearance,
        IconElement? icon,
        TimeSpan timeout
    )
    {
        if (_presenter is null)
        {
            throw new InvalidOperationException($"The SnackbarPresenter was never set");
        }

        _snackbar ??= new Snackbar(_presenter);
        // ...
    }
}

Available services:

Interface Implementation Purpose
INavigationService NavigationService Page navigation
IContentDialogService ContentDialogService Modal content dialogs
ISnackbarService SnackbarService Toast-like snackbar notifications
ITaskBarService TaskBarService Windows taskbar integration
IThemeService ThemeService Theme management

2.10 Error Handling

Win32/Interop code -- Bare catch blocks swallowing exceptions (intentional for cross-OS compatibility):

csharp
catch
{
    // Ignore registry access errors on non-Windows or restricted environments
    return null;
}

Service code -- Throw ArgumentNullException / InvalidOperationException for precondition violations:

csharp
if (_presenter is null)
{
    throw new InvalidOperationException($"The SnackbarPresenter was never set");
}
csharp
protected void ThrowIfNavigationControlIsNull()
{
    if (NavigationControl is null)
    {
        throw new ArgumentNullException(nameof(NavigationControl));
    }
}

2.11 Win32 Interop

The project uses a three-layer architecture for native Windows API access.

Layer 1: CsWin32 auto-generated P/Invoke via Microsoft.Windows.CsWin32 NuGet package. Native function names are listed in src/Wpf.Ui/NativeMethods.txt.

Layer 2: Managed wrappers with validation (src/Wpf.Ui/Interop/UnsafeNativeMethods.cs):

csharp
internal static class UnsafeNativeMethods
{
    public static unsafe bool ApplyWindowCornerPreference(
        IntPtr handle,
        WindowCornerPreference cornerPreference
    )
    {
        // ALWAYS validate handle before calling Win32 APIs
        if (handle == IntPtr.Zero)
        {
            return false;
        }

        if (!PInvoke.IsWindow(new HWND(handle)))
        {
            return false;
        }

        DWM_WINDOW_CORNER_PREFERENCE pvAttribute = UnsafeReflection.Cast(cornerPreference);

        return PInvoke.DwmSetWindowAttribute(
                new HWND(handle),
                DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
                &pvAttribute,
                sizeof(int)
            ) == HRESULT.S_OK;
    }
}

Layer 3: Utility helpers (src/Wpf.Ui/Win32/):

csharp
internal sealed class Utilities
{
    public static bool IsOSWindows11OrNewer => _osVersion.Build >= 22000;
}

Critical rule: Always validate handles (IntPtr.Zero check + PInvoke.IsWindow) before any native API call. Always search the existing codebase before assuming standard WPF approaches work for TitleBar, window management, system theme detection, or DWM integration.

2.12 Test Naming

Unit tests use MethodName_ExpectedResult_WhenCondition:

csharp
public class TransitionAnimationProviderTests
{
    [Fact]
    public void ApplyTransition_ReturnsFalse_WhenDurationIsLessThan10()
    {
        UIElement mockedUiElement = Substitute.For<UIElement>();

        var result = TransitionAnimationProvider.ApplyTransition(mockedUiElement, Transition.FadeIn, -10);

        Assert.False(result);
    }

    [Fact]
    public void ApplyTransition_ReturnsFalse_WhenElementIsNull()
    {
        var result = TransitionAnimationProvider.ApplyTransition(null, Transition.FadeIn, 1000);

        Assert.False(result);
    }
}

Integration tests use the UiTest base class with FlaUI and AwesomeAssertions:

csharp
public sealed class TitleBarTests : UiTest
{
    [Fact]
    public async Task CloseButton_ShouldCloseWindow_WhenClicked()
    {
        Button? closeButton = FindFirst("TitleBarCloseButton").AsButton();

        closeButton.Should().NotBeNull("because CloseButton should be present in the main window title bar");
        closeButton.Click(moveMouse: false);

        await Wait(2);

        Application
            ?.HasExited.Should()
            .BeTrue("because the main window should be closed after clicking the close button");
    }
}

The Gallery app uses CommunityToolkit.Mvvm. ViewModels are partial classes inheriting from ViewModel (which extends ObservableObject).

ViewModel (src/Wpf.Ui.Gallery/ViewModels/Pages/BasicInput/AnchorViewModel.cs):

csharp
namespace Wpf.Ui.Gallery.ViewModels.Pages.BasicInput;

public partial class AnchorViewModel : ViewModel
{
    [ObservableProperty]
    private bool _isAnchorEnabled = true;

    [RelayCommand]
    private void OnAnchorCheckboxChecked(object sender)
    {
        if (sender is not CheckBox checkbox)
        {
            return;
        }

        IsAnchorEnabled = !(checkbox?.IsChecked ?? false);
    }
}

Page (src/Wpf.Ui.Gallery/Views/Pages/BasicInput/AnchorPage.xaml.cs):

csharp
namespace Wpf.Ui.Gallery.Views.Pages.BasicInput;

[GalleryPage("Button which opens a link.", SymbolRegular.CubeLink20)]
public partial class AnchorPage : INavigableView<AnchorViewModel>
{
    public AnchorViewModel ViewModel { get; init; }

    public AnchorPage(AnchorViewModel viewModel)
    {
        ViewModel = viewModel;
        DataContext = this;

        InitializeComponent();
    }
}

Key MVVM conventions:

  • [ObservableProperty] for _camelCase backing fields (generates PascalCase properties)
  • [RelayCommand] for On-prefixed private methods (generates {Name}Command ICommand)
  • Pattern matching with is not for type checks in command handlers
  • DataContext = this set in page constructor (not in XAML)
  • INavigableView<TViewModel> interface for navigation-aware pages

2.14 Central Package Management

All NuGet package versions are centralized in Directory.Packages.props. Individual .csproj files reference packages without version attributes.

Directory.Packages.props (versions defined here):

xml
<Project>
  <ItemGroup>
    <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
    <PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.242" />
    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
    <PackageVersion Include="WpfAnalyzers" Version="4.1.1" />
    <PackageVersion Include="xunit.v3" Version="3.2.0" />
    
  </ItemGroup>
</Project>

.csproj (NO version attribute):

xml
<PackageReference Include="Microsoft.Windows.CsWin32">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

2.15 Commit Convention

Format: type(scope): description (#PR_NUMBER)

Type Use For
feat New feature
fix Bug fix
chore Build/tooling changes
Scope Use For
controls Control-related changes
app Gallery app changes
docs Documentation changes

Examples from actual commits:

text
fix(controls): Fix TextBlock issues (#1640)
fix(controls): Remove AnimationFactorToValueConverter from CheckBox.xaml (#1649)
fix(controls): NavigationViewItemAutomationPeer followup (#1646)
fix(controls): Update ContentDialog For TitleBar CenterContent (#1642)
feat(app): Add ability to disable automatic apply of system accent color in UiApplication (#1643)

3. Boundary System

3.1 ALWAYS DO

These practices are mandatory. Violations will be caught by analyzers, build errors, or code review.

# Rule Enforcement
A1 Use file-scoped namespaces in all C# files .editorconfig csharp_style_namespace_declarations = file_scoped:warning
A2 Enable nullable reference types Directory.Build.props <Nullable>enable</Nullable>
A3 Add MIT license file header to every .cs and .xaml file .editorconfig IDE0073 = error
A4 Add XML docs with `` and <example> tags for all public APIs Code review
A5 Use CSharpier for formatting (110 char, 4-space indent) .csharpierrc, CI
A6 Use Central Package Management (versions in Directory.Packages.props only) Directory.Build.props <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
A7 Validate Win32 handles before any native API call (IntPtr.Zero + PInvoke.IsWindow) Code review
A8 Use pattern matching (is not) over type checks with casts .editorconfig csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
A9 Use DependencyProperty.Register with nameof() Code review, WpfAnalyzers
A10 Include XAML example in XML docs for controls Code review
A11 Follow commit convention: type(scope): description (#PR_NUMBER) Code review
A12 Search existing codebase before assuming standard WPF approaches work for Win32 Code review
A13 Use _camelCase for private fields .editorconfig naming rules
A14 Set OverridesDefaultStyle=True and SnapsToDevicePixels=True in control styles Code review
A15 Use DynamicResource for theme-dependent values, StaticResource for constants Code review
A16 Place each control in its own subfolder under Controls/ Project convention

3.2 ASK FIRST

These actions require discussion with the maintainer before proceeding.

# Action Reason
Q1 Adding a new NuGet package dependency Impacts all consumers; increases dependency surface area
Q2 Modifying Win32/Interop layer May affect multiple OS versions; requires testing on Windows 10/11
Q3 Changing public API surface Breaking change potential for library consumers
Q4 Modifying TitleBar or window management code Complex Win32 interaction with WndProc message handling, snap layouts, DWM
Q5 Adding new multi-target framework conditional compilation Increases maintenance burden across 6 TFMs
Q6 Modifying theme/accent color management System-wide effects; affects all controls
Q7 Changing NavigationView behavior Most complex control in the library (27+ DependencyProperties, 7+ RoutedEvents, 6 partial files)
Q8 Modifying assembly signing or packaging configuration Impacts NuGet distribution and strong-name consumers

3.3 NEVER DO

These practices are forbidden. Violations will be rejected in code review.

# Rule Enforcement
N1 Never add package versions directly to .csproj files Central Package Management enabled
N2 Never use var for non-apparent types .editorconfig csharp_style_var_elsewhere = false:warning
N3 Never use primary constructors .editorconfig IDE0290 = none (suppressed)
N4 Never add comments that merely restate what the code does Code review
N5 Never use emoticons in code or comments Code review
N6 Never assume library/NuGet availability without checking Directory.Packages.props Code review
N7 Never skip handle validation in Win32 interop code Code review
N8 Never use expression-bodied members for constructors, methods, or properties .editorconfig csharp_style_expression_bodied_* = false:silent
N9 Never modify namespace to match folder structure for Controls .editorconfig IDE0130 = none (suppressed); flat Wpf.Ui.Controls namespace
N10 Never use this. qualification .editorconfig SA1101 = none; dotnet_style_qualification = false:silent

Concrete examples of NEVER DO violations and their corrections:

csharp
// N1 VIOLATION: version in .csproj
<PackageReference Include="NSubstitute" Version="5.3.0" />
// CORRECT: no version attribute
<PackageReference Include="NSubstitute" />

// N2 VIOLATION: var for non-apparent type
var service = container.Resolve<ISnackbarService>();
// CORRECT: explicit type
ISnackbarService service = container.Resolve<ISnackbarService>();

// N3 VIOLATION: primary constructor
public class MyService(ILogger logger) : IMyService { }
// CORRECT: traditional constructor
public class MyService : IMyService
{
    private readonly ILogger _logger;

    public MyService(ILogger logger)
    {
        _logger = logger;
    }
}

// N7 VIOLATION: no handle validation
PInvoke.DwmSetWindowAttribute(new HWND(handle), attr, &value, sizeof(int));
// CORRECT: validate first
if (handle == IntPtr.Zero) return false;
if (!PInvoke.IsWindow(new HWND(handle))) return false;
PInvoke.DwmSetWindowAttribute(new HWND(handle), attr, &value, sizeof(int));

// N8 VIOLATION: expression-bodied property
public string Name => _name;
// CORRECT: block body
public string Name
{
    get => _name;
}

// N9 VIOLATION: folder-matching namespace
namespace Wpf.Ui.Controls.Button;
// CORRECT: flat Controls namespace
// ReSharper disable once CheckNamespace
namespace Wpf.Ui.Controls;

// N10 VIOLATION: this. qualification
this._name = name;
// CORRECT: no this.
_name = name;

4. Rules Files Summary

The project enforces conventions through multiple configuration files working in concert.

Configuration Files Overview

File Location Purpose
.editorconfig / Encoding, indent rules, naming rules, diagnostic severities
.csharpierrc / CSharpier formatter configuration
Directory.Build.props / Version, lang version, nullable, unsafe blocks, CPM
Directory.Build.targets / SourceLink, assembly signing, analyzers, trimming
Directory.Packages.props / Centralized NuGet package versions
.github/copilot-instructions.md /.github/ GitHub Copilot AI agent instructions
CLAUDE.md / Claude Code AI agent guidance
.github/pull_request_template.md /.github/ PR template with type checkboxes

`.editorconfig` -- Key Rules

Encoding and Indentation:

File Type Indent Size
C# (*.cs) Spaces 4
XML/JSON/YAML (*.xml, *.json, *.yml) Spaces 2
MSBuild (*.csproj, *.props, *.targets) Spaces 2
Solutions (*.sln, *.slnx) Tabs 4
Markdown (*.md) Spaces 2

Enforced Diagnostics (error/warning level):

Diagnostic Severity Description
IDE0073 error File header template (MIT license)
csharp_style_namespace_declarations warning File-scoped namespaces
csharp_style_var_elsewhere warning No var for non-apparent types
dotnet_style_predefined_type_for_locals_parameters_members warning Language keywords over BCL types
dotnet_style_readonly_field warning Readonly fields where possible
csharp_prefer_static_local_function warning Static local functions
csharp_style_pattern_local_over_anonymous_function warning Local functions over lambdas
csharp_using_directive_placement warning Usings outside namespace

Suppressed Diagnostics:

Diagnostic Reason
SA1101 No this. qualification required
SA1309 Allow underscore-prefixed field names
SA1600 Relaxed XML documentation enforcement (not all members require docs)
CS1591 Missing XML comment (15000+ existing warnings)
IDE0130 Namespace not matching folder structure (flat Wpf.Ui.Controls)
IDE0290 Primary constructors not used
CA1510 ArgumentNullException.ThrowIfNull helper not available on older TFMs
WPF0070-0073 ValueConversion attribute warnings relaxed

Naming Rules:

Symbol Style Example
Constants PascalCase MaxRetryCount
Public/internal/protected fields PascalCase DefaultTimeOut
Public static readonly fields PascalCase IconProperty
Private/protected fields _camelCase _presenter
Public methods/properties/events PascalCase Navigate()
Parameters camelCase contentPresenter
Interfaces I + PascalCase ISnackbarService

`.csharpierrc` -- Formatter Configuration

json
{
    "printWidth": 110,
    "useTabs": false,
    "tabWidth": 4,
    "preprocessorSymbolSets": [
        "",
        "DEBUG",
        "DEBUG,CODE_STYLE"
    ]
}

Run formatter: dotnet csharpier .

`Directory.Build.props` -- Central Build Configuration

Property Value Effect
Version 4.2.0 Assembly and package version
LangVersion 14.0 C# 14 language features
Nullable enable Nullable reference types globally enabled
AllowUnsafeBlocks true Required for Win32 interop
ManagePackageVersionsCentrally true Central Package Management
EnforceCodeStyleInBuild true Analyzer warnings fail the build
GenerateDocumentationFile true For core projects only
PackageLicenseExpression MIT License metadata

`Directory.Build.targets` -- Build Targets

Key responsibilities:

  • Analyzer packages: AsyncFixer, IDisposableAnalyzers, StyleCop.Analyzers added to all projects (except .dcproj, .sfproj, VS template/extension projects)
  • PolySharp: Added for netstandard2.0/2.1 and net462/472/481 targets (backports modern C# features)
  • SourceLink: GitHub SourceLink for NuGet packages
  • Assembly signing: Conditional on SourceLinkEnabled=true AND GeneratePackageOnBuild=true, using src/lepo.snk
  • Trimming/AOT: Enabled for .NET 6+ targets (IsTrimmable, EnableTrimAnalyzer, EnableAotAnalyzer, EnableSingleFileAnalyzer)
  • Commit hash: Embedded as AssemblyMetadataAttribute("CommitHash", ...) via SourceRevisionId

`Directory.Packages.props` -- Centralized Package Versions

All NuGet package versions for the entire solution are defined here. Key packages:

Package Version Purpose
CommunityToolkit.Mvvm 8.4.0 MVVM toolkit for Gallery/samples
Microsoft.Windows.CsWin32 0.3.242 P/Invoke source generator
StyleCop.Analyzers 1.2.0-beta.556 Code style analyzer
WpfAnalyzers 4.1.1 WPF-specific analyzers
AsyncFixer 1.6.0 Async/await analyzer
IDisposableAnalyzers 4.0.8 IDisposable pattern analyzer
xunit.v3 3.2.0 Unit test framework
NSubstitute 5.3.0 Mocking framework
AwesomeAssertions 9.3.0 Fluent assertions
FlaUI.Core / FlaUI.UIA3 5.0.0 UI automation testing
PolySharp 1.15.0 C# feature polyfills for older TFMs
Microsoft.Extensions.Hosting 10.0.0 .NET Generic Host

`.github/copilot-instructions.md` -- AI Agent Instructions

Provides GitHub Copilot with:

  • Project structure and build commands
  • Code conventions (never assume library availability, modern C#)
  • XML documentation requirements with concrete examples
  • Windows platform guidance (search before assuming WPF approaches work)
  • Tone/style: no emoticons, minimize output, no preamble/postamble
  • MVVM patterns for Gallery pages
  • Testing patterns (unit + integration)

`CLAUDE.md` -- Claude Code Guidance

Provides Claude Code with:

  • Build commands for all solution configurations
  • Architecture overview (project structure, core library layout)
  • Key patterns (Win32 interop, XAML resources, MVVM, CPM)
  • Testing frameworks and conventions
  • Code conventions summary
  • Commit convention format

`.github/pull_request_template.md` -- PR Template

PR type checkboxes:

  • Update
  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)
  • Build related changes
  • Documentation content changes

Required sections:

  • What is the current behavior? (with Issue Number field)
  • What is the new behavior?
  • Other information (screenshots)

Version Information

Field Value
Library Version 4.2.0
Language Version C# 14 (preview)
Target Frameworks .NET 10/9/8, .NET Framework 4.8.1/4.7.2/4.6.2
NuGet Package ID WPF-UI
XAML Namespace http://schemas.lepo.co/wpfui/2022/xaml
XAML Prefix ui
Constitution Version 1.0 (2025-02-10)

Architecture IMPLEMENTATION GUIDE (`docs/architecture/IMPLEMENTATION-GUIDE.md`)

WPF UI Implementation Guide

Step-by-step guides for common implementation tasks in the WPF UI project.

Table of Contents

  1. How to Add a New Control
  2. How to Add a New Service
  3. How to Add a Gallery Page
  4. How to Use Win32 Interop
  5. How to Add Theme Support
  6. Testing a Feature

1. How to Add a New Control

This guide walks through creating a new control following the WPF UI conventions.

Step 1: Create Control Folder Structure

text
src/Wpf.Ui/Controls/
└── MyControl/
    ├── MyControl.cs       # Code-behind class
    └── MyControl.xaml     # Style and ControlTemplate

Step 2: Create the Control Class (MyControl.cs)

text
/* Detailed source-code truncated for AI context efficiency. */

Step 3: Create the XAML Style (MyControl.xaml)

text
/* Detailed source-code truncated for AI context efficiency. */

Step 4: Register in Wpf.Ui.xaml

Add reference to src/Wpf.Ui/Resources/Wpf.Ui.xaml:

xml
<ResourceDictionary.MergedDictionaries>
    
    <ResourceDictionary Source="pack://application:,,,/Wpf.Ui;component/Controls/MyControl/MyControl.xaml" />
</ResourceDictionary.MergedDictionaries>

Step 5: Add Designer Support (Optional)

Add toolbox icon bitmap to src/Wpf.Ui/Assets/Toolbox/MyControl.bmp (16x16 pixels).

Register in src/Wpf.Ui/VisualStudioToolsManifest.xml:

xml
<ToolboxItems UIFramework="WPF" VSCategory="WPF UI" BlendCategory="WPF UI">
    <Item Type="Wpf.Ui.Controls.MyControl" />
</ToolboxItems>

Step 6: Add to Category Documentation

Document the control category in architecture notes for future reference.


2. How to Add a New Service

This guide shows how to create a new service following the WPF UI service pattern.

Step 1: Create the Service Interface (IMyService.cs)

csharp
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

namespace Wpf.Ui;

/// <summary>
/// Provides functionality for managing custom operations.
/// </summary>
public interface IMyService
{
    /// <summary>
    /// Gets the current state of the service.
    /// </summary>
    bool IsActive { get; }

    /// <summary>
    /// Initializes the service with the specified parameter.
    /// </summary>
    /// <param name="parameter">The initialization parameter.</param>
    void Initialize(string parameter);

    /// <summary>
    /// Performs an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    Task<bool> PerformOperationAsync(CancellationToken cancellationToken = default);
}

Step 2: Create the Service Implementation (MyService.cs)

csharp
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Wpf.Ui;

/// <summary>
/// Implementation of <see cref="IMyService"/>.
/// </summary>
public partial class MyService : IMyService
{
    private string? _parameter;
    private bool _isActive;

    /// <inheritdoc />
    public bool IsActive => _isActive;

    /// <inheritdoc />
    public void Initialize(string parameter)
    {
        ArgumentNullException.ThrowIfNull(parameter);

        _parameter = parameter;
        _isActive = true;
    }

    /// <inheritdoc />
    public async Task<bool> PerformOperationAsync(CancellationToken cancellationToken = default)
    {
        ThrowIfNotInitialized();

        try
        {
            // Perform async operation
            await Task.Delay(100, cancellationToken);
            return true;
        }
        catch (OperationCanceledException)
        {
            return false;
        }
    }

    private void ThrowIfNotInitialized()
    {
        if (!_isActive || _parameter is null)
        {
            throw new InvalidOperationException("Service has not been initialized. Call Initialize() first.");
        }
    }
}

Step 3: Add DI Extension Method

Create src/Wpf.Ui.DependencyInjection/ServiceCollectionExtensions.MyService.cs:

csharp
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using Microsoft.Extensions.DependencyInjection;
using Wpf.Ui;

namespace Wpf.Ui.DependencyInjection;

/// <summary>
/// Extension methods for registering WPF UI services.
/// </summary>
public static partial class ServiceCollectionExtensions
{
    /// <summary>
    /// Registers <see cref="IMyService"/> as a singleton.
    /// </summary>
    /// <param name="services">The service collection.</param>
    /// <returns>The service collection for chaining.</returns>
    public static IServiceCollection AddMyService(this IServiceCollection services)
    {
        _ = services.AddSingleton<IMyService, MyService>();
        return services;
    }
}

Step 4: Register in Application

csharp
// In your application startup (e.g., App.xaml.cs or Program.cs)
services.AddMyService();

Step 5: Use the Service

csharp
public partial class MyViewModel : ViewModel
{
    private readonly IMyService _myService;

    public MyViewModel(IMyService myService)
    {
        _myService = myService;
        _myService.Initialize("parameter-value");
    }

    [RelayCommand]
    private async Task OnPerformAction()
    {
        bool result = await _myService.PerformOperationAsync();
        // Handle result
    }
}

This guide demonstrates adding a new demo page to the WPF UI Gallery application.

Step 1: Create ViewModel

Create src/Wpf.Ui.Gallery/ViewModels/Pages/MyCategory/MyControlViewModel.cs:

text
/* Detailed source-code truncated for AI context efficiency. */

Step 2: Create Page (XAML)

Create src/Wpf.Ui.Gallery/Views/Pages/MyCategory/MyControlPage.xaml:

text
/* Detailed source-code truncated for AI context efficiency. */

Step 3: Create Page Code-Behind

Create src/Wpf.Ui.Gallery/Views/Pages/MyCategory/MyControlPage.xaml.cs:

csharp
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using Wpf.Ui.Controls;
using Wpf.Ui.Gallery.Attributes;
using Wpf.Ui.Gallery.ViewModels.Pages.MyCategory;

namespace Wpf.Ui.Gallery.Views.Pages.MyCategory;

[GalleryPage("Custom control demonstration.", SymbolRegular.Heart24)]
public partial class MyControlPage : INavigableView<MyControlViewModel>
{
    public MyControlViewModel ViewModel { get; }

    public MyControlPage(MyControlViewModel viewModel)
    {
        ViewModel = viewModel;
        DataContext = this;
        InitializeComponent();
    }
}

Step 4: Register in DI Container

Add to src/Wpf.Ui.Gallery/App.xaml.cs or DI configuration:

csharp
// Register ViewModel
services.AddSingleton<MyControlViewModel>();

// Register Page
services.AddSingleton<MyControlPage>();

Step 5: Add Navigation Entry

If you need manual navigation registration (not using automatic discovery), add to navigation setup:

csharp
navigationService.SetNavigationControl(navigationView);

// Register page type
menuItems.Add(new NavigationViewItem
{
    Content = "MyControl",
    Icon = new SymbolIcon { Symbol = SymbolRegular.Heart24 },
    TargetPageType = typeof(MyControlPage)
});

4. How to Use Win32 Interop

This guide demonstrates the three-layer Win32 interop architecture used in WPF UI.

Step 1: Add Function to NativeMethods.txt

Edit src/Wpf.Ui/NativeMethods.txt:

text
// Existing entries...
DwmSetWindowAttribute
IsWindow

// Add your new function
GetSystemMetrics

This triggers CsWin32 to auto-generate P/Invoke declarations in the Windows.Win32 namespace.

Step 2: Create Managed Wrapper (if needed)

If you need a safe wrapper with handle validation, add to src/Wpf.Ui/Interop/UnsafeNativeMethods.cs:

csharp
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;

namespace Wpf.Ui.Interop;

internal static partial class UnsafeNativeMethods
{
    /// <summary>
    /// Safely retrieves a system metric value with handle validation.
    /// </summary>
    /// <param name="handle">Window handle for context (optional).</param>
    /// <param name="metric">The system metric to retrieve.</param>
    /// <returns>The metric value, or 0 if the operation fails.</returns>
    public static int GetSystemMetricSafe(IntPtr handle, SYSTEM_METRICS_INDEX metric)
    {
        // Validate handle if provided
        if (handle != IntPtr.Zero && !PInvoke.IsWindow(new HWND(handle)))
        {
            return 0;
        }

        try
        {
            return PInvoke.GetSystemMetrics(metric);
        }
        catch
        {
            // Swallow exception for OS compatibility (intentional)
            return 0;
        }
    }
}

Step 3: Create Utility Helper (if needed)

If you need higher-level utility functions, add to src/Wpf.Ui/Win32/Utilities.cs:

csharp
using Wpf.Ui.Interop;
using Windows.Win32.UI.WindowsAndMessaging;

namespace Wpf.Ui.Win32;

internal sealed class Utilities
{
    /// <summary>
    /// Gets the width of the window border.
    /// </summary>
    public static int BorderWidth => UnsafeNativeMethods.GetSystemMetricSafe(
        IntPtr.Zero,
        SYSTEM_METRICS_INDEX.SM_CXBORDER
    );

    /// <summary>
    /// Gets the height of the window border.
    /// </summary>
    public static int BorderHeight => UnsafeNativeMethods.GetSystemMetricSafe(
        IntPtr.Zero,
        SYSTEM_METRICS_INDEX.SM_CYBORDER
    );
}

Step 4: Use in Your Code

csharp
using Wpf.Ui.Interop;
using Wpf.Ui.Win32;
using Windows.Win32;
using Windows.Win32.Foundation;

// Option 1: Direct P/Invoke (for simple cases)
int screenWidth = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN);

// Option 2: Safe wrapper (validates handle, catches exceptions)
int borderWidth = UnsafeNativeMethods.GetSystemMetricSafe(
    windowHandle,
    SYSTEM_METRICS_INDEX.SM_CXBORDER
);

// Option 3: High-level utility
int borderHeight = Utilities.BorderHeight;

Step 5: Handle WndProc Messages (if needed)

To intercept Windows messages:

csharp
using System.Windows.Interop;
using Windows.Win32.UI.WindowsAndMessaging;

public class MyWindow : Window
{
    private HwndSource? _hwndSource;

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        _hwndSource = (HwndSource)PresentationSource.FromVisual(this);
        _hwndSource?.AddHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch ((uint)msg)
        {
            case PInvoke.WM_DWMCOLORIZATIONCOLORCHANGED:
                // Handle theme change
                handled = true;
                break;

            case PInvoke.WM_THEMECHANGED:
                // Handle system theme change
                handled = true;
                break;
        }

        return IntPtr.Zero;
    }

    protected override void OnClosed(EventArgs e)
    {
        _hwndSource?.RemoveHook(WndProc);
        base.OnClosed(e);
    }
}

Important Notes

  1. Always validate handles before calling Win32 APIs:

    csharp
    if (handle == IntPtr.Zero || !PInvoke.IsWindow(new HWND(handle)))
        return false;
  2. Catch exceptions in interop code for OS compatibility (some APIs may not exist on older Windows versions)

  3. Use unsafe keyword for pointer operations

  4. Check OS version before using new APIs:

    csharp
    if (Wpf.Ui.Win32.Utilities.IsOSWindows11OrNewer)
    {
        // Use Windows 11-specific API
    }

5. How to Add Theme Support

This guide shows how to make your control theme-aware.

Step 1: Use DynamicResource in XAML

xml
<Style TargetType="{x:Type controls:MyControl}">
    
    <Setter Property="Background" Value="{DynamicResource ControlFillColorDefaultBrush}" />
    <Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
    <Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}" />

    
    <Setter Property="Padding" Value="{StaticResource MyControlPadding}" />
</Style>

Step 2: Subscribe to Theme Changes (if needed)

If your control needs to react to theme changes programmatically:

csharp
using Wpf.Ui.Appearance;

public class MyControl : ContentControl
{
    public MyControl()
    {
        ApplicationThemeManager.Changed += OnThemeChanged;
    }

    private void OnThemeChanged(ApplicationTheme currentTheme, Color systemAccent)
    {
        // React to theme change
        UpdateVisuals(currentTheme);
    }

    private void UpdateVisuals(ApplicationTheme theme)
    {
        // Update control state based on theme
        if (theme == ApplicationTheme.Dark)
        {
            // Dark theme-specific logic
        }
        else
        {
            // Light theme-specific logic
        }
    }
}

Step 3: Use Theme-Aware Brushes

Available dynamic brush resources:

Background Brushes:

  • ApplicationBackgroundBrush
  • ControlFillColorDefaultBrush
  • ControlFillColorSecondaryBrush
  • ControlFillColorTertiaryBrush
  • ControlFillColorDisabledBrush

Foreground Brushes:

  • TextFillColorPrimaryBrush
  • TextFillColorSecondaryBrush
  • TextFillColorTertiaryBrush
  • TextFillColorDisabledBrush

Accent Brushes:

  • AccentFillColorDefaultBrush
  • AccentFillColorSecondaryBrush
  • AccentFillColorTertiaryBrush
  • SystemAccentColor (Color, not Brush)

Border Brushes:

  • ControlElevationBorderBrush
  • ControlStrokeColorDefaultBrush
  • ControlStrokeColorSecondaryBrush

Step 4: Implement IThemeControl (if applicable)

csharp
public class MyControl : ContentControl, IThemeControl
{
    /// <summary>Identifies the <see cref="Theme"/> dependency property.</summary>
    public static readonly DependencyProperty ThemeProperty = DependencyProperty.Register(
        nameof(Theme),
        typeof(ApplicationTheme),
        typeof(MyControl),
        new PropertyMetadata(ApplicationTheme.Light, OnThemeChanged)
    );

    /// <summary>
    /// Gets or sets the theme of the control.
    /// </summary>
    public ApplicationTheme Theme
    {
        get => (ApplicationTheme)GetValue(ThemeProperty);
        set => SetValue(ThemeProperty, value);
    }

    private static void OnThemeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MyControl control)
        {
            control.UpdateTheme((ApplicationTheme)e.NewValue);
        }
    }

    private void UpdateTheme(ApplicationTheme theme)
    {
        // Update control based on theme
    }
}

Step 5: Test Theme Switching

csharp
// Apply theme programmatically
ApplicationThemeManager.Apply(ApplicationTheme.Dark);

// Apply system accent color
ApplicationAccentColorManager.ApplySystemAccent();

// Watch for system theme changes
SystemThemeWatcher.Watch(myWindow);

6. Testing a Feature

This guide covers both unit testing and integration testing for WPF UI.

Unit Test Example

Create tests/Wpf.Ui.UnitTests/Controls/MyControlTests.cs:

text
/* Detailed source-code truncated for AI context efficiency. */

Integration Test Example

Create tests/Wpf.Ui.Gallery.IntegrationTests/MyControlTests.cs:

text
/* Detailed source-code truncated for AI context efficiency. */

Test Infrastructure

Base Test Class (tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs):

csharp
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.UIA3;
using Xunit;

namespace Wpf.Ui.Gallery.IntegrationTests.Fixtures;

public abstract class UiTest : IAsyncLifetime
{
    protected Application? Application { get; private set; }
    protected Window Window { get; private set; } = null!;
    private Process? _process;

    public async Task InitializeAsync()
    {
        // Launch Gallery application
        var appPath = GetApplicationPath();
        _process = Process.Start(appPath);

        await Task.Delay(2000); // Wait for app to start

        // Attach to application
        Application = FlaUI.Core.Application.Attach(_process);

        using var automation = new UIA3Automation();
        Window = Application.GetMainWindow(automation);
    }

    public async Task DisposeAsync()
    {
        Application?.Close();
        Application?.Dispose();

        if (_process != null && !_process.HasExited)
        {
            _process.Kill();
            _process.Dispose();
        }

        await Task.CompletedTask;
    }

    private static string GetApplicationPath()
    {
        // Path to Gallery executable
        return Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory,
            "..\\..\\..\\..\\..\\src\\Wpf.Ui.Gallery\\bin\\Debug\\net10.0-windows\\Wpf.Ui.Gallery.exe"
        );
    }
}

Running Tests

bash
# Run all tests
dotnet test

# Run only unit tests
dotnet test tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj

# Run only integration tests
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/Wpf.Ui.Gallery.IntegrationTests.csproj

# Run with code coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

Common Pitfalls to Avoid

  1. Don't add package versions to .csproj - use Directory.Packages.props only

  2. Don't forget handle validation in Win32 interop:

    csharp
    if (handle == IntPtr.Zero || !PInvoke.IsWindow(new HWND(handle)))
        return false;
  3. Don't use var for non-apparent types (causes warning)

  4. Don't forget MIT license header (causes error via IDE0073)

  5. Don't modify control namespace to match folder structure - use flat Wpf.Ui.Controls

  6. Don't use StaticResource for theme-dependent values - use DynamicResource

  7. Don't forget to register pages and ViewModels in DI container

  8. Don't use this. qualification (SA1101 suppressed)

  9. Don't add XML docs that restate code - explain WHY, not WHAT

  10. Don't skip the [GalleryPage] attribute on Gallery pages


Quick Reference Checklist

Adding a Control

  • Create folder under Controls/
  • Create .cs file with control class
  • Create .xaml file with style
  • Implement interfaces (IAppearanceControl, IIconControl)
  • Register DependencyProperties with XML docs
  • Override DefaultStyleKeyProperty
  • Add to Wpf.Ui.xaml MergedDictionaries
  • Add toolbox icon (optional)
  • Add unit tests

Adding a Service

  • Create I{Name}Service.cs interface
  • Create {Name}Service.cs implementation
  • Add DI extension method in Wpf.Ui.DependencyInjection
  • Add XML docs with examples
  • Add unit tests
  • Document in CLAUDE.md
  • Create ViewModel in ViewModels/Pages/{Category}/
  • Create Page.xaml in Views/Pages/{Category}/
  • Create Page.xaml.cs code-behind
  • Add [GalleryPage] attribute
  • Register ViewModel and Page in DI
  • Add navigation entry (if manual)
  • Add code examples with syntax:CodeBlock

Architecture MODULE INTERFACES (`docs/architecture/MODULE-INTERFACES.md`)

Module Interfaces

WPF UI v4.2.0 | Public and internal API surface per module

This document defines the public vs internal API surface for each module in the WPF UI solution. It serves as the contract specification for inter-module communication and consumer-facing APIs.


Module Dependency Graph

mermaid
graph TD
    subgraph "Public API Surface"
        Abstractions["<b>Wpf.Ui.Abstractions</b><br/><i>Public: 6 types</i><br/><i>Internal: none</i>"]
        Core["<b>Wpf.Ui</b><br/><i>Public: 77+ controls, 6 services,<br/>managers, converters, markup</i><br/><i>Internal: Win32, Interop</i>"]
        DI["<b>Wpf.Ui.DependencyInjection</b><br/><i>Public: 2 types</i><br/><i>Internal: none</i>"]
        Tray["<b>Wpf.Ui.Tray</b><br/><i>Public: 4 types</i><br/><i>Internal: 7 types</i>"]
        Syntax["<b>Wpf.Ui.SyntaxHighlight</b><br/><i>Public: 2 types</i><br/><i>Internal: 2 types</i>"]
    end

    subgraph "Non-Distributable"
        Toast["<b>Wpf.Ui.ToastNotifications</b><br/><i>Public: 1 type (STUB)</i>"]
        FlaUI["<b>Wpf.Ui.FlaUI</b><br/><i>Public: 1 type</i>"]
        FontMapper["<b>Wpf.Ui.FontMapper</b><br/><i>Build-time tool</i>"]
    end

    Core --> Abstractions
    DI --> Abstractions
    Tray --> Core
    Syntax --> Core

    style Abstractions fill:#e1f5ff,stroke:#0277bd
    style Core fill:#fff4e1,stroke:#f57f17
    style DI fill:#e8f5e9,stroke:#2e7d32
    style Tray fill:#e8f5e9,stroke:#2e7d32
    style Syntax fill:#e8f5e9,stroke:#2e7d32
    style Toast fill:#fff9c4,stroke:#f9a825
    style FlaUI fill:#f3e5f5,stroke:#6a1b9a
    style FontMapper fill:#f3e5f5,stroke:#6a1b9a

Module: Wpf.Ui.Abstractions

NuGet: WPF-UI.Abstractions
TFMs: net10.0, net9.0, net8.0, net462, netstandard2.1, netstandard2.0
Dependencies: None (zero external dependencies)

Public Types

Type Kind Description
INavigationViewPageProvider Interface Resolves page instances by type for NavigationView
INavigableView<T> Interface Associates a view with its ViewModel type
INavigationAware Interface Lifecycle callbacks for navigation (OnNavigatedTo/From)
NavigationAware Abstract class Base implementation of INavigationAware
NavigationException Exception Thrown when navigation operations fail
NavigationViewPageProviderExtensions Static class Extension methods for INavigationViewPageProvider

Internal Types

None. This module is entirely public by design -- it defines the contract surface.


Module: Wpf.Ui (Core)

NuGet: WPF-UI
TFMs: net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462
Dependencies: Wpf.Ui.Abstractions, Microsoft.Windows.CsWin32 (build-time), System.Memory

Public Namespaces

Namespace Contents
Wpf.Ui UiApplication, service interfaces and implementations
Wpf.Ui.Controls 77+ Fluent Design controls
Wpf.Ui.Appearance ApplicationThemeManager, ApplicationAccentColorManager, SystemThemeWatcher, WindowBackgroundManager
Wpf.Ui.Converters 18 IValueConverter implementations
Wpf.Ui.Markup ControlsDictionary, ThemesDictionary, SymbolIconExtension, FontIconExtension, ImageIconExtension
Wpf.Ui.Extensions 14 extension method classes
Wpf.Ui.Input IRelayCommand, IRelayCommand<T>, RelayCommand<T>

Public Service Interfaces

Interface Implementation Wraps
INavigationService NavigationService INavigationView control
IContentDialogService ContentDialogService ContentDialog control
ISnackbarService SnackbarService Snackbar control
IThemeService ThemeService ApplicationThemeManager (static)
ITaskBarService TaskBarService COM ITaskbarList4
INavigationWindow — Interface for windows hosting NavigationView

Internal / Should-Be-Internal Namespaces

Namespace Status Notes
Wpf.Ui.Interop Currently public Managed Win32 wrappers (UnsafeNativeMethods, PInvoke). Recommended for internalization (see RECOMMENDATIONS.md)
Wpf.Ui.Win32 Currently public OS version utilities. Recommended for internalization

Note: Wpf.Ui.Interop and Wpf.Ui.Win32 expose raw P/Invoke declarations that are implementation details. Consumers should not depend on these namespaces. A future major version should mark them internal.


Module: Wpf.Ui.DependencyInjection

NuGet: WPF-UI.DependencyInjection
TFMs: net10.0, net9.0, net8.0, net462, netstandard2.1, netstandard2.0
Dependencies: Wpf.Ui.Abstractions, Microsoft.Extensions.DependencyInjection.Abstractions 3.1.0

Public Types

Type Kind Description
ServiceCollectionExtensions Static class AddNavigationViewPageProvider<T>() extension method for IServiceCollection
DependencyInjectionNavigationViewPageProvider Class INavigationViewPageProvider implementation that resolves pages via IServiceProvider

Internal Types

None.


Module: Wpf.Ui.Tray

NuGet: WPF-UI.Tray
TFMs: net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462
Dependencies: Wpf.Ui, System.Drawing.Common

Public Types

Type Kind Description
INotifyIconService Interface Service interface for tray icon management
NotifyIconService Class INotifyIconService implementation
NotifyIcon Control WPF control for declarative tray icon in XAML
RoutedNotifyIconEvent Delegate Event delegate for tray icon interactions

Internal Types

Type Kind Description
INotifyIcon Interface Internal abstraction for tray icon operations
TrayHandler Class Shell32 Shell_NotifyIcon P/Invoke wrapper
TrayManager Class Tray icon lifecycle management
TrayData Struct Native tray icon data structure
Hicon Struct Icon handle wrapper
NotifyIconEventHandler Delegate Internal event handler
InternalNotifyIconManager Class Theme-aware tray icon management

Module: Wpf.Ui.SyntaxHighlight

NuGet: WPF-UI.SyntaxHighlight
TFMs: net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462
Dependencies: Wpf.Ui

Public Types

Type Kind Description
CodeBlock Control WPF control for syntax-highlighted code display
SyntaxHighlightDictionary Markup Extension XAML resource dictionary for syntax highlighting styles

Internal Types

Type Kind Description
Highlighter Class Regex-based syntax highlighting engine
SyntaxLanguage Enum Supported language identifiers

Module: Wpf.Ui.ToastNotifications

NuGet: WPF-UI.ToastNotifications
TFMs: net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462
Dependencies: None (standalone stub)

Public Types

Type Kind Description
Toast Class STUB — all methods throw NotImplementedException

Warning: This module is a placeholder with no implementation. See RECOMMENDATIONS.md — recommended to either implement or remove.


Module: Wpf.Ui.FlaUI

NuGet: WPF-UI.FlaUI
TFMs: net10.0-windows, net9.0-windows, net8.0-windows, net481
Dependencies: FlaUI.Core

Public Types

Type Kind Description
AutoSuggestBox Class FlaUI automation element wrapper for Wpf.Ui.Controls.AutoSuggestBox

Module: Wpf.Ui.FontMapper

Type: Build-time console tool (not a distributable library)
TFMs: net10.0
Dependencies: None

Public Types

None. This is a build tool that generates SymbolRegular and SymbolFilled enums from Fluent System Icons font JSON data. It is not referenced by other projects at runtime.


Cross-Module Interface Rules

  1. Abstractions are the only shared contract. Modules that need to communicate must do so through Wpf.Ui.Abstractions interfaces.
  2. Core library is the integration point. Only Wpf.Ui may depend on Win32 interop and WPF framework internals.
  3. Satellite packages depend downward only. Tray and SyntaxHighlight depend on Core; they never depend on each other.
  4. DI package depends on Abstractions only. Wpf.Ui.DependencyInjection must never take a dependency on Wpf.Ui to keep it lightweight.
  5. Internal namespaces are not API surface. Types in Wpf.Ui.Interop and Wpf.Ui.Win32 are implementation details even though they are currently public.

Architecture RECOMMENDATIONS (`docs/architecture/RECOMMENDATIONS.md`)

WPF UI - Architecture Recommendations

Generated: 2026-02-10. These recommendations are based on architectural analysis and should be re-evaluated as the codebase evolves.

Recommendations

Testing

Priority Recommendation Rationale Affected
Critical Add unit tests for core controls, targeting at least NavigationView, TitleBar, FluentWindow, ContentDialog, NumberBox, and ToggleSwitch. Only 6 unit tests exist for a 77+ control library. Regressions ship undetected. src/Wpf.Ui/Controls/, tests/Wpf.Ui.UnitTests/
Critical Enable test execution in the PR validator CI workflow. The current workflow builds the Gallery app but never runs tests. Merging broken code is possible. .github/workflows/
High Add integration tests for NavigationView page caching and lifecycle transitions. Caching behavior is undocumented and untested. Users report inconsistent state across navigation. src/Wpf.Ui/Controls/NavigationView/
Medium Add contract tests for INavigationService, IContentDialogService, and ISnackbarService. Service interfaces are the primary public API surface for DI consumers but lack test coverage. src/Wpf.Ui.Abstractions/, src/Wpf.Ui/Services/

Architecture

Priority Recommendation Rationale Affected
High Wrap static theme managers (ApplicationThemeManager, ApplicationAccentColorManager) with injectable service interfaces. Static managers cannot be mocked or unit tested. IThemeService exists but the underlying managers remain untestable. src/Wpf.Ui/Appearance/
High Document NavigationView page caching strategy: when pages are created, cached, and disposed. Caching behavior is implicit in the implementation with no documentation or configuration surface. src/Wpf.Ui/Controls/NavigationView/
Medium Add structured logging (e.g., ILogger) to Win32 interop error-handling paths. Catch blocks intentionally swallow Win32 exceptions but produce no diagnostics. Silent failures make debugging difficult for consumers. src/Wpf.Ui/Interop/, src/Wpf.Ui/Win32/

Technical Debt

Priority Recommendation Rationale Affected
High Either implement Wpf.Ui.ToastNotifications or remove it from the solution. The project is a stub with no implementation. It ships as a package that does nothing. src/Wpf.Ui.ToastNotifications/
Medium Audit and reduce public API surface of Win32/Interop namespaces. Many P/Invoke declarations are public but intended for internal use only. Exposing raw Win32 types couples consumers to implementation details. src/Wpf.Ui/Win32/, src/Wpf.Ui/Interop/
Low Consolidate resource dictionary loading paths. ThemesDictionary and ControlsDictionary have overlapping responsibilities that could confuse consumers. src/Wpf.Ui/Resources/

CI/CD

Priority Recommendation Rationale Affected
Critical Add a CI job that runs dotnet test tests/Wpf.Ui.UnitTests/ on every PR. No tests run in CI. The existing test suite provides zero protection against regressions. .github/workflows/
High Add code coverage reporting (Coverlet is already configured) and set a minimum threshold. Coverage tooling is present but never executed. Without a baseline, coverage can only decrease. tests/Wpf.Ui.UnitTests/, .github/workflows/
Medium Add a CI step to run dotnet csharpier --check . to enforce formatting. CSharpier is configured but not enforced in CI. Formatting inconsistencies can slip through review. .github/workflows/

Documentation

Priority Recommendation Rationale Affected
Medium Add XML doc <example> tags with XAML usage to all public control APIs. Project conventions require examples, but coverage is inconsistent across the 77+ controls. src/Wpf.Ui/Controls/
Low Create a process for keeping architecture docs in sync with code changes. Architecture documentation (generated 2026-02-10) will drift from the implementation without a manual update process or automation. docs/architecture/

Task Backlog

Structured work items derived from the recommendations above. Each item includes acceptance criteria (AC).

TB-001: Add unit tests for core controls [Critical]

Source: Testing #1
AC:

  • NavigationView has ≥5 unit tests covering navigation, caching, back stack
  • TitleBar has ≥3 tests covering minimize/maximize/close commands
  • FluentWindow has ≥2 tests covering backdrop type selection
  • ContentDialog has ≥3 tests covering show/hide/result lifecycle
  • NumberBox has ≥3 tests covering min/max/step validation
  • ToggleSwitch has ≥2 tests covering checked/unchecked state
  • All tests follow MethodName_ExpectedResult_WhenCondition naming convention
  • All tests use XUnit v3, NSubstitute, AwesomeAssertions

TB-002: Enable test execution in CI [Critical]

Source: CI/CD #1, Testing #2
AC:

  • .github/workflows/ contains a job that runs dotnet test tests/Wpf.Ui.UnitTests/ on every PR
  • CI job fails the PR if any test fails
  • Test results are visible in the GitHub Actions summary
  • Job runs on ubuntu-latest or windows-latest as appropriate for WPF tests

TB-003: Add code coverage reporting with threshold [High]

Source: CI/CD #2
AC:

  • Coverlet generates coverage report during CI test run
  • Coverage report is uploaded as a CI artifact or displayed in PR summary
  • Minimum coverage threshold is set (≥50% for initial baseline)
  • Build fails if coverage drops below threshold

TB-004: Enforce CSharpier formatting in CI [Medium]

Source: CI/CD #3
AC:

  • CI job runs dotnet csharpier --check . on every PR
  • PR fails if any file is not formatted
  • Contributing guide documents the dotnet csharpier . command

TB-005: Wrap static theme managers with injectable interfaces [High]

Source: Architecture #1
AC:

  • IApplicationThemeManager interface exists with Apply(), GetAppTheme(), and Changed event
  • IApplicationAccentColorManager interface exists with Apply() and GetColorizationColor()
  • Default implementations delegate to existing static classes
  • Interfaces are registered in DI via ServiceCollectionExtensions
  • Existing static API remains functional (non-breaking change)

TB-006: Document NavigationView page caching strategy [High]

Source: Architecture #2
AC:

  • XML doc comments on NavigationCacheMode enum explain each mode
  • docs/architecture/cross-cutting/navigation.md describes caching lifecycle
  • Gallery demo includes a page demonstrating cache mode differences
  • At least one unit test verifies cache behavior per mode

TB-007: Implement or remove Wpf.Ui.ToastNotifications [High]

Source: Technical Debt #1
AC:

  • Decision documented: implement with Windows App SDK toast APIs OR remove from solution
  • If implemented: at least one functional toast notification scenario works end-to-end
  • If removed: NuGet package is delisted, project removed from solution, references cleaned up

TB-008: Audit and reduce Win32/Interop public API surface [Medium]

Source: Technical Debt #2
AC:

  • All types in Wpf.Ui.Interop and Wpf.Ui.Win32 are marked internal or documented as public API
  • [EditorBrowsable(EditorBrowsableState.Never)] added to types that must remain public for binary compat
  • No consumer-facing documentation references internal interop types

TB-009: Add integration tests for NavigationView caching [High]

Source: Testing #3
AC:

  • FlaUI integration test navigates forward/backward and verifies page instance identity (cached vs new)
  • Test covers NavigationCacheMode.Enabled, Disabled, and Required
  • Test verifies INavigationAware.OnNavigatedTo/OnNavigatedFrom callback order

TB-010: Add contract tests for service interfaces [Medium]

Source: Testing #4
AC:

  • INavigationService has ≥3 contract tests (Navigate, GoBack, SetService)
  • IContentDialogService has ≥2 contract tests (ShowAsync, SetDialogHost)
  • ISnackbarService has ≥2 contract tests (Show, SetPresenter)
  • Tests verify interface contracts, not implementation details
  • Tests use NSubstitute for mock dependencies

Architecture TESTING SPEC (`docs/architecture/TESTING-SPEC.md`)

Testing Specification for AI Agents

Overview

This document provides testing guidelines for AI agents working on the WPF UI project. The project uses separate unit and integration test suites with distinct frameworks and patterns.

Test Project Structure

Unit Tests

Project: tests/Wpf.Ui.UnitTests/
Target Framework: net10.0-windows
Project Reference: Wpf.Ui

Integration Tests

Project: tests/Wpf.Ui.Gallery.IntegrationTests/
Target Framework: net10.0-windows10.0.26100.0
Project References: Wpf.Ui.FlaUI, Wpf.Ui.Gallery

Testing Frameworks

Unit Testing Stack

  • XUnit v2-style (PackageReference: xunit, xunit.runner.visualstudio)
  • NSubstitute 5.3.0 - Mocking framework
  • Standard XUnit Assert - Assertion library
  • Coverlet 6.0.4 - Code coverage collector

Integration Testing Stack

  • XUnit v3 (PackageReference: xunit.v3, xunit.runner.visualstudio)
  • FlaUI.UIA3 5.0.0 - UI automation framework
  • AwesomeAssertions 9.3.0 - Fluent assertion library (FluentAssertions successor)
  • Custom Wpf.Ui.FlaUI - Custom automation element wrappers

Unit Test Template

Naming Convention

text
MethodName_ExpectedResult_WhenCondition

Alternative format:

text
GivenCondition_MethodName_ExpectedResult

Basic Structure

csharp
using Xunit;
using NSubstitute;
using Wpf.Ui.Animations; // Namespace under test

namespace Wpf.Ui.UnitTests.Animations;

public class TransitionAnimationProviderTests
{
    [Fact]
    public void ApplyTransition_ReturnsFalse_WhenDurationIsLessThan10()
    {
        // Arrange
        UIElement mockedUiElement = Substitute.For<UIElement>();

        // Act
        var result = TransitionAnimationProvider.ApplyTransition(
            mockedUiElement,
            Transition.FadeIn,
            -10
        );

        // Assert
        Assert.False(result);
    }

    [Fact]
    public void ApplyTransition_ReturnsFalse_WhenElementIsNull()
    {
        // Arrange
        UIElement? nullElement = null;

        // Act
        var result = TransitionAnimationProvider.ApplyTransition(
            nullElement,
            Transition.FadeIn,
            100
        );

        // Assert
        Assert.False(result);
    }
}

Mocking with NSubstitute

csharp
// Create mock
UIElement element = Substitute.For<UIElement>();

// Setup return value
INavigationService service = Substitute.For<INavigationService>();
service.Navigate(typeof(DashboardPage)).Returns(true);

// Verify call
service.Received(1).Navigate(Arg.Any<Type>());

Global Usings

Located in tests/Wpf.Ui.UnitTests/Usings.cs:

csharp
global using System;
global using System.Windows;
global using NSubstitute;
global using Xunit;

Integration Test Template

Naming Convention

text
Subject_ShouldExpectedBehavior_WhenCondition

Base Class Pattern

All integration tests inherit from UiTest:

csharp
using AwesomeAssertions;
using FlaUI.Core.AutomationElements;
using FlaUI.UIA3.Patterns;

namespace Wpf.Ui.Gallery.IntegrationTests;

public sealed class NavigationTests : UiTest
{
    [Fact]
    public async Task Settings_ShouldBeAvailable_ThroughAutoSuggestBox()
    {
        // Arrange
        Wpf.Ui.FlaUI.AutoSuggestBox? autoSuggestBox =
            FindFirst("NavigationAutoSuggestBox")?.As<AutoSuggestBox>();

        autoSuggestBox.Should().NotBeNull(
            "because AutoSuggestBox should be present in the navigation bar"
        );

        // Act
        autoSuggestBox!.Enter("Settings");
        await Wait(1);

        // Assert
        TextBox? pageTitle = FindFirst("PageTitle")?.AsTextBox();
        pageTitle.Should().NotBeNull();
        pageTitle!.Text.Should().Be("Settings");
    }
}

UiTest Base Class Features

Location: tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs

Provided Methods:

csharp
// Find element by automation ID
protected AutomationElement? FindFirst(string automationId)

// Find element by condition
protected AutomationElement? FindFirst(Func<ConditionFactory, ConditionBase> buildCondition)

// Wait for specified seconds
protected async Task Wait(int seconds, CancellationToken cancellationToken = default)

// Type text
protected void Enter(string text)

// Press key
protected void Press(VirtualKeyShort key)

Lifecycle:

  • IAsyncLifetime implementation
  • Each test gets its own application instance
  • Application launched via TestedApplication fixture
  • Automatic cleanup after test completion

AwesomeAssertions Syntax

csharp
// Null checks
element.Should().NotBeNull("because element must exist");
element.Should().BeNull();

// String assertions
text.Should().Be("Expected");
text.Should().Contain("substring");
text.Should().StartWith("prefix");

// Boolean assertions
condition.Should().BeTrue("because condition must be met");
Application?.HasExited.Should().BeTrue();

// Collection assertions
items.Should().HaveCount(5);
items.Should().Contain(item);

FlaUI Element Access

csharp
// Find and cast to specific control
Button? button = FindFirst("ButtonId").AsButton();
TextBox? textBox = FindFirst("TextBoxId")?.AsTextBox();

// Custom automation elements
var autoSuggestBox = FindFirst("AutoSuggestBoxId")?.AsAutoSuggestBox();

// Interact with controls
button.Click(moveMouse: false);
textBox.Text = "value";

// Pattern-based interaction
var invokePattern = element.Patterns.Invoke.Pattern;
invokePattern.Invoke();

Running Tests

Unit Tests

bash
# Run all unit tests
dotnet test tests/Wpf.Ui.UnitTests/

# Run specific test class
dotnet test tests/Wpf.Ui.UnitTests/ --filter "FullyQualifiedName~TransitionAnimationProviderTests"

# Run with coverage
dotnet test tests/Wpf.Ui.UnitTests/ --collect:"XPlat Code Coverage"

Integration Tests

bash
# Run all integration tests
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/

# Run specific test
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --filter "FullyQualifiedName~TitleBarTests"

# Run with diagnostics
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --logger "console;verbosity=detailed"

XUnit Runner Configuration

Location: tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json

json
{
  "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
  "parallelizeTestCollections": false,
  "diagnosticMessages": true,
  "culture": "invariant"
}

Important: Integration tests do NOT run in parallel due to single Gallery app instance.

Test Organization

Namespace Mirroring

Test namespaces mirror source namespaces:

text
src/Wpf.Ui/Animations/TransitionAnimationProvider.cs
↓
tests/Wpf.Ui.UnitTests/Animations/TransitionAnimationProviderTests.cs

File Naming

text
{ClassName}Tests.cs

Test Coverage Areas

Current Unit Test Coverage:

  • Animations: TransitionAnimationProvider
  • Extensions: SymbolExtensions.Swap(), SymbolExtensions.GetString()

Current Integration Test Coverage:

  • Window title verification
  • TitleBar button interaction (Close, Minimize, Maximize)
  • Navigation via AutoSuggestBox
  • Navigation via NavigationView
  • ContentDialog result verification
  • ContentDialog keyboard focus isolation

Guidelines for AI Agents

When Writing Unit Tests

  1. Mock dependencies using NSubstitute
  2. Test public API surface only
  3. Use XUnit Assert methods for assertions
  4. Follow naming convention strictly
  5. One assertion per test (where logical)
  6. Test both success and failure paths

When Writing Integration Tests

  1. Inherit from UiTest base class
  2. Use automation IDs for element discovery
  3. Add delays with Wait() for UI updates
  4. Use AwesomeAssertions fluent syntax
  5. Provide clear "because" messages in assertions
  6. Clean up state (handled automatically by fixture)

Test File Template Locations

Unit Test Template:

text
tests/Wpf.Ui.UnitTests/Animations/TransitionAnimationProviderTests.cs
tests/Wpf.Ui.UnitTests/Extensions/SymbolExtensionsTests.cs

Integration Test Template:

text
tests/Wpf.Ui.Gallery.IntegrationTests/TitleBarTests.cs
tests/Wpf.Ui.Gallery.IntegrationTests/NavigationTests.cs

Common Patterns

Testing Dependency Properties

csharp
[Fact]
public void PropertyName_DefaultValue_IsExpected()
{
    var control = new MyControl();
    Assert.Equal(expectedDefault, control.PropertyName);
}

[Fact]
public void PropertyName_CanBeSet_AndRetrieved()
{
    var control = new MyControl();
    var expectedValue = new SomeType();

    control.PropertyName = expectedValue;

    Assert.Equal(expectedValue, control.PropertyName);
}

Testing Services

csharp
[Fact]
public void Navigate_ReturnsTrue_WhenNavigationSucceeds()
{
    // Arrange
    var pageProvider = Substitute.For<INavigationViewPageProvider>();
    pageProvider.GetPage(Arg.Any<Type>()).Returns(new DashboardPage());

    var service = new NavigationService(pageProvider);
    var navigationView = Substitute.For<INavigationView>();
    service.SetNavigationControl(navigationView);

    // Act
    bool result = service.Navigate(typeof(DashboardPage));

    // Assert
    Assert.True(result);
}

Continuous Integration

Tests are NOT currently run in CI (PR validator only builds Gallery app).

To add test execution to CI, modify .github/workflows/wpf-ui-pr-validator.yaml:

yaml
- name: Run Unit Tests
  run: dotnet test tests/Wpf.Ui.UnitTests/ --no-restore --verbosity normal

- name: Run Integration Tests
  run: dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/ --no-restore --verbosity normal

Architecture UNCERTAINTIES (`docs/architecture/UNCERTAINTIES.md`)

Architecture Uncertainties

This document collects areas where the architecture documentation is incomplete or uncertain based on the analysis. These items require further investigation or are subject to change.

Core Library (Group 1)

Test Coverage

Area: tests/ directory (outside analysis scope)

Cannot determine exact test coverage. Given 77+ controls with complex state management (NavigationView journal, ContentDialog async lifecycle), the testability of static managers (ApplicationThemeManager, SystemThemeWatcher) is limited. Service interfaces (INavigationService, IContentDialogService, etc.) are testable through mock implementations.

Current unit test coverage is minimal (6 tests covering Animations and Extensions only).

WindowBackdrop Implementation

Area: src/Wpf.Ui/Controls/Window/WindowBackdrop.cs

WindowBackdrop class is referenced in FluentWindow and WindowBackgroundManager (ApplyBackdrop, RemoveBackdrop, RemoveBackground, RemoveTitlebarBackground) but was not directly examined. Likely located in Controls/Window/ directory and provides Mica/Acrylic/Tabbed backdrop effects via DWM APIs.

Area: src/Wpf.Ui/Controls/NavigationView/NavigationCache.cs and NavigationCacheMode.cs

These files exist but were not deeply examined. The caching strategy for navigation pages includes Required, Enabled, and Disabled modes but the exact implementation details need further investigation.

PolySharp Configuration

Area: Central Package Management in Directory.Packages.props

PolySharp is referenced in core library csproj via PolySharpExcludeGeneratedTypes but the actual package reference appears to come from Directory.Packages.props (central package management) which was not fully examined. The exact polyfill configuration and version require verification.

Satellite Libraries (Group 2)

Toast Notifications Implementation Status

Module: Wpf.Ui.ToastNotifications

It is unclear whether this library has any concrete implementation plans or if it is purely a placeholder for future work. The Toast.Show() method throws NotImplementedException. The library has no dependency on Wpf.Ui core despite being a WPF-targeting project.

FlaUI Library Scope

Module: Wpf.Ui.FlaUI

The FlaUI library has no project reference to Wpf.Ui, yet it exists under the Wpf.Ui namespace. It only depends on FlaUI.Core. Currently contains only an AutoSuggestBox automation element wrapper. It is unclear whether additional automation elements for other WPF UI controls will be added.

FontMapper Integration Workflow

Module: Wpf.Ui.FontMapper

The FontMapper generates enum files for the core library but has no project reference to it. The generated output path uses a relative 'generated/' directory. The exact integration workflow (how generated files reach the core library) is not evident from the project analysis alone.

Additional Issue: FetchVersion() returns a hardcoded version '1.1.316' instead of actually fetching from GitHub API (the API call code is commented out).

SyntaxHighlight Font Dependency

Module: Wpf.Ui.SyntaxHighlight

The SyntaxHighlight.xaml references a FontFamily with key 'FiraCode' using pack URI pointing to Wpf.Ui (not Wpf.Ui.SyntaxHighlight), suggesting the font may need to also exist in the core library.

Additional Issue: The CodeBlock.cs is also listed as an EmbeddedResource in the csproj, which is unusual for a source file.

WIP Status: Highlighter class is marked as work in progress. The autodetect language feature defaults to XAML, and C# and XAML use identical regex patterns.

Module: Wpf.Ui.Gallery.Package

A .wapproj (Windows Application Packaging) project is referenced in Wpf.Ui.Gallery.slnf but not directly analyzed. This likely creates an MSIX package for the Gallery app. The packaging configuration and deployment process need clarification.

GalleryAssembly Reference

Area: src/Wpf.Ui.Gallery/ControlsLookup/

Referenced as GalleryAssembly.Asssembly (note triple 's') in DependencyModel and ControlsLookup. The actual GalleryAssembly class was not directly read. It likely provides the Gallery assembly reference for reflection-based page discovery.

ReflectionEventing Usage

Package: ReflectionEventing and ReflectionEventing.DependencyInjection

These packages are referenced in the Gallery csproj but their usage was not traced to specific event handling code within the analyzed files. The purpose and integration point of these packages require investigation.

Visual Studio Extension

Module: src/Wpf.Ui.Extension

The VS2022 extension project (.vsix) is built for x64 and arm64 platforms but was not analyzed. The extension capabilities and integration points with Visual Studio are unknown.

Build and Infrastructure

.NET SDK Version Mismatch

Area: build.ps1

The build script references .NET SDK 8 for installation (winget install Microsoft.DotNet.SDK.8) but the project now targets .NET 10. The build script may be outdated.

Dependabot Target Branch

Area: .github/dependabot.yml

The Dependabot configuration targets the 'development' branch, not 'main'. The branch strategy and merge workflow from development to main are not documented.

CI Test Execution

Area: .github/workflows/wpf-ui-pr-validator.yaml

The PR validator workflow only builds the Gallery app and does not run any tests (unit or integration). The rationale for not including test execution in CI is unclear.

Strong-Name Signing

Area: GitHub Actions secrets and certificate management

The CD pipeline fetches a strong-name certificate from GitHub secrets (${{ secrets.WPF_UI_CERTIFICATE_BASE64 }}). The certificate generation, storage, and rotation procedures are not documented.

Documentation and Versioning

Version Synchronization

Area: Directory.Build.props vs actual package versions

The Version property is set to 4.2.0, but the synchronization mechanism between this central version, individual package versions, and changelog updates is not documented.

API Compatibility

Area: Deprecated APIs and migration paths

Several APIs are marked [Obsolete] (ContentDialog legacy constructors, WindowBackgroundManager.UpdateBackground with forceBackground parameter). The deprecation timeline and migration strategy for consumers are not documented.

Automation Peer Coverage

Area: src/Wpf.Ui/AutomationPeers/

Only 4 automation peers exist for 77+ controls:

  • CardControlAutomationPeer
  • ContentDialogAutomationPeer
  • CardActionAutomationPeer
  • NavigationViewItemAutomationPeer

Many interactive controls (Button, NavigationView base, TextBox, etc.) may lack custom automation peer implementations. The strategy for expanding accessibility support is unclear. This potentially affects screen reader compatibility and UI automation testing.

Theme Resource Keys

Area: Dynamic resource naming and contracts

20+ accent-related dynamic resources are updated programmatically by ApplicationAccentColorManager:

  • SystemAccentColor
  • AccentFillColorDefault
  • TextOnAccentFillColorPrimary
  • (and others)

The complete list of dynamic resources and their contracts (expected types, update triggers) is not centrally documented. Third-party theme authors may struggle to determine which resources must be provided.

Multi-Target Conditional Compilation

Area: Cross-framework API compatibility

Various conditional compilation symbols are used:

  • NET5_0_OR_GREATER
  • NET6_0_OR_GREATER
  • NET8_0_OR_GREATER
  • NET48_OR_GREATER_or_NETCOREAPP3_0_OR_GREATER

The decision matrix for when to use each symbol and the feature availability across frameworks is not documented in a single location.

System Requirements

Area: Minimum Windows version and feature matrix

The library supports Windows 7 through Windows 11, but feature availability varies:

  • Mica/Acrylic backdrops require Windows 11
  • Some DWM features require Windows 10
  • Certain APIs have Fall Creators Update requirements

A comprehensive feature compatibility matrix by Windows version is not documented.


Documentation About Wpf (`docs/documentation/about-wpf.md`)

What is WPF

WPF (Windows Presentation Foundation) is a resolution-independent UI framework for building Windows desktop applications. It provides vector-based graphics, advanced layout, data binding, multimedia, animation, and extensive styling and templating capabilities.

.NET Framework vs Modern .NET

WPF has two implementations:

Modern .NET (.NET 6+): Open-source implementation hosted on GitHub. Provides improved performance, new APIs, side-by-side deployment, and modern tooling. Despite .NET being cross-platform, WPF only runs on Windows.

.NET Framework 4: Legacy Windows-only implementation. Maintained for compatibility with existing applications but receives minimal new features.

New WPF applications should target modern .NET (.NET 6 or later) to benefit from:

  • Better performance and reduced memory usage
  • Regular updates and new features
  • Modern C# language versions
  • Improved debugging and diagnostics
  • Side-by-side deployment without machine-wide installations

Documentation Accent (`docs/documentation/accent.md`)

Accent Colors

Accent colors provide visual emphasis and brand identity in WPF UI applications. The library manages accent color resources that automatically adapt to light and dark themes.

[!TIP]
Use SystemThemeWatcher.Watch(this) in your main window constructor to automatically sync accent colors with Windows personalization settings.

Apply System Accent

Use the system's personalization accent color:

csharp
using Wpf.Ui.Appearance;

ApplicationAccentColorManager.ApplySystemAccent();

Apply Theme with Accent

Apply theme and accent together:

csharp
using Wpf.Ui.Appearance;
using Wpf.Ui.Controls;

ApplicationThemeManager.Apply(
    ApplicationTheme.Dark,
    WindowBackdropType.Mica,
    updateAccent: true  // Automatically applies system accent
);

Available backdrop types: None, Auto, Mica, Acrylic, Tabbed.

[!IMPORTANT]
Always use DynamicResource (not StaticResource) for accent color bindings to receive runtime updates when accent changes.

Apply Custom Accent

Set a custom accent color:

csharp
ApplicationAccentColorManager.Apply(
    Color.FromArgb(0xFF, 0xEE, 0x00, 0xBB),
    ApplicationTheme.Dark
);

Retrieve the Windows colorization color programmatically:

csharp
Color colorizationColor = ApplicationAccentColorManager.GetColorizationColor();
ApplicationAccentColorManager.Apply(colorizationColor, ApplicationTheme.Dark);

Accent Color Resources

WPF UI provides these accent color resources:

System Accent Colors

Base accent colors that update when you call ApplicationAccentColorManager.Apply():

  • SystemAccentColor - Primary system accent
  • SystemAccentColorPrimary - Lighter/darker variant for light/dark themes
  • SystemAccentColorSecondary - More prominent variant (most commonly used)
  • SystemAccentColorTertiary - Strongest variant
xml
<Border Background="{DynamicResource SystemAccentColorSecondaryBrush}" />

[!TIP]
SystemAccentColorSecondary is the most commonly used variant for interactive elements and provides optimal contrast in both light and dark themes.

Accent Text Colors

For text and interactive elements like links:

  • AccentTextFillColorPrimaryBrush - Primary accent text (rest/hover state)
  • AccentTextFillColorSecondaryBrush - Secondary accent text
  • AccentTextFillColorTertiaryBrush - Tertiary accent text (pressed state)
  • AccentTextFillColorDisabledBrush - Disabled accent text
xml
<ui:Anchor
    Content="WPF UI Documentation"
    NavigateUri="https://wpfui.lepo.co/"
    Foreground="{DynamicResource AccentTextFillColorPrimaryBrush}" />

Accent Fill Colors

For button backgrounds and filled surfaces:

  • AccentFillColorDefaultBrush - Default accent fill
  • AccentFillColorSecondaryBrush - Secondary fill (90% opacity)
  • AccentFillColorTertiaryBrush - Tertiary fill (80% opacity)
  • AccentFillColorDisabledBrush - Disabled state fill
xml
<Button Background="{DynamicResource AccentFillColorDefaultBrush}" />

Text on Accent Colors

For text displayed on accent-colored backgrounds. These resources automatically adjust to black or white based on accent brightness:

  • TextOnAccentFillColorPrimary - Primary text color on accent backgrounds
  • TextOnAccentFillColorSecondary - Secondary text on accent backgrounds
  • TextOnAccentFillColorDisabled - Disabled text on accent backgrounds

[!NOTE]
Text colors automatically switch between black and white when accent brightness exceeds 80% HSV to maintain readability.

Theme-Specific Behavior

Accent variants adjust automatically based on the application theme:

Dark Theme:

  • Primary: Base color + 17 brightness, -30% saturation
  • Secondary: Base color + 17 brightness, -45% saturation
  • Tertiary: Base color + 17 brightness, -65% saturation

Light Theme:

  • Primary: Base color - 10 brightness
  • Secondary: Base color - 25 brightness
  • Tertiary: Base color - 40 brightness

[!NOTE]
Brightness adjustments are calculated in HSV color space. Negative values darken the color, positive values lighten it.

Advanced: Custom Accent Variants

Specify all accent variants manually:

csharp
ApplicationAccentColorManager.Apply(
    systemAccent: Color.FromArgb(0xFF, 0x00, 0x78, 0xD4),
    primaryAccent: Color.FromArgb(0xFF, 0x00, 0x67, 0xC0),
    secondaryAccent: Color.FromArgb(0xFF, 0x00, 0x3E, 0x92),
    tertiaryAccent: Color.FromArgb(0xFF, 0x00, 0x1A, 0x68)
);

[!CAUTION]
Manually specified accent variants won't automatically adjust when theme changes. Consider using automatic variant generation unless you need precise color control.

Accessing Current Accent

Read current accent colors from ApplicationAccentColorManager:

csharp
Color currentSystemAccent = ApplicationAccentColorManager.SystemAccent;
Color currentPrimaryAccent = ApplicationAccentColorManager.PrimaryAccent;
Color currentSecondaryAccent = ApplicationAccentColorManager.SecondaryAccent;
Color currentTertiaryAccent = ApplicationAccentColorManager.TertiaryAccent;

Brush accentBrush = ApplicationAccentColorManager.SystemAccentBrush;

Theme Changed Event

Monitor accent changes when theme is applied:

csharp
ApplicationThemeManager.Changed += (theme, accent) =>
{
    Debug.WriteLine($"Theme changed to {theme}, accent: {accent}");
};

Automatic System Theme Tracking

Use SystemThemeWatcher to automatically update theme and accent when Windows settings change:

csharp
using Wpf.Ui.Appearance;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        // Watch system theme changes with Mica backdrop and accent updates
        SystemThemeWatcher.Watch(this);
        
        InitializeComponent();
    }
}

With custom backdrop and accent settings:

csharp
SystemThemeWatcher.Watch(
    this,
    WindowBackdropType.Acrylic,
    updateAccents: true
);

Stop watching for theme changes:

csharp
SystemThemeWatcher.UnWatch(this);

[!NOTE]
SystemThemeWatcher monitors WM_WININICHANGE messages and automatically applies system theme and accent when Windows personalization settings change.


Documentation Extension (`docs/documentation/extension.md`)

Visual Studio 2022 Extension for WPF UI

Visual Studio allows you to add extensions that can be installed in several ways:

  • Build them locally and then install the .vsix package.
  • Download the extension from the internet and install the .vsix file.
  • Install the extension using the search in Visual Studio.

In this tutorial, we'll cover the last way, if you want to know more, check out Manage extensions for Visual Studio.

In any case, if you want to download a plugin and install it manually, or leave your review, you can find it in the Visual Studio Marketplace:
https://marketplace.visualstudio.com/items?itemName=lepo.wpf-ui

[!NOTE]
The source code for WPF UI Visual Studio 2022 extension is public and you can check it out here.

How to?

  1. Install Visual Studio 2022 from Visual Studio 2022 downloads.

  2. After installation, open Visual Studio

  3. Expand the Extensions tab in the menu and then click Manage Extensions

  4. In the Online tab, use the search engine to enter WPF-UI in it, then click Download

  5. After downloading, restart Visual Studio

  6. After restarting, you will see a window asking you to confirm the installation.

  7. Once installed, you can restart Visual Studio and click Create new project

  8. In the top right corner, you can select the project type

  9. Voila, you have just installed the WPF UI extension.

Done!

After creating a project, you can familiarize yourself with its structure and proceed to further steps.


Documentation Fonticon (`docs/documentation/fonticon.md`)

FontIcon

FontIcon is a control responsible for rendering icons based on the provided font.

Implementation

csharp
class Wpf.Ui.Controls.FontIcon

Exposes

csharp
// Gets or sets displayed glyph
FontIcon.Glyph = '\uE00B';
csharp
// Gets or sets used font family
FontIcon.FontFamily = "Segoe Fluent Icons";
csharp
// Icon foreground
FontIcon.Foreground = Brushes.White;
csharp
// Icon size
FontIcon.FontSize = 16;

How to use

xml
<ui:FontIcon
  Glyph="&#xe00b;"
  FontFamily="{DynamicResource SegoeFluentIcons}"
  FontSize="16"
  Foreground="White"/>

WPF UI Gallery is a free application available in the Microsoft Store, with which you can test all functionalities.
https://apps.microsoft.com/store/detail/wpf-ui/9N9LKV8R9VGM

powershell
$ winget install 'WPF UI'

Documentation Icons (`docs/documentation/icons.md`)

Fluent System Icons

Fluent System Icons is a set of icons that is designed to be used with Microsoft's Fluent Design System. It is a collection of over 1,500 icons that are designed to be modern, consistent, and scalable, and can be used in a variety of applications and platforms, including web and mobile applications.

The Fluent System Icons set includes a range of icons, such as those for basic navigation, media playback, communication, and more. The icons are available in various sizes, from 16x16 to 512x512 pixels, and are provided in vector format, allowing for easy scaling and customization.

Fluent System Icons is available for free and can be downloaded from the official Microsoft website. It is also open source, meaning that developers can contribute to the icon set or create their own custom icons based on the existing ones.
Fluent UI System Icons

WPF UI uses Fluent UI System Icons in most of the graphical controls.

Getting started

Icons are displayed by using the font that comes with the library. All glyphs are mapped to the SymbolRegular and SymbolFilled enums.

Icon controls and fonts will be automatically added to your application if you add ControlsDictionary in the App.xaml file:

xml
<Application
    ...
    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
    <Application.Resources>
        <ui:ThemesDictionary Theme="Dark" />
        <ui:ControlsDictionary />
    </Application.Resources>
</Application>

[!NOTE]
You can find out how the Control Dictionary works here

Segoe Fluent Icons

Not all icons available in WinUi 3 are in Fluent UI System Icons. Some of them require the Segoe Fluent Icons font.
According to the EULA of Segoe Fluent Icons we cannot ship a copy of it with this dll. Segoe Fluent Icons is installed by default on Windows 11, but if you want these icons in an application for Windows 10 and below, you must manually add the font to your application's resources.
https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font
https://docs.microsoft.com/en-us/windows/apps/design/downloads/#fonts

In the App.xaml dictionaries, you can add an alternate path to the font

xml
<Application
    ...
    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
    <Application.Resources>
        <ui:ThemesDictionary Theme="Dark" />
        <ui:ControlsDictionary />

        <FontFamily x:Key="SegoeFluentIcons">pack://application:,,,/;component/Fonts/#Segoe Fluent Icons</FontFamily>
    </Application.Resources>
</Application>

Documentation Navigation View (`docs/documentation/navigation-view.md`)

NavigationView is a top-level navigation control that provides a collapsible navigation pane (the "hamburger menu") and a content area. It is the primary way to implement top-level navigation in your app.

[!TIP]
For a complete implementation example, see the WPF UI Gallery application.

Anatomy

The NavigationView control has several key areas:

  • Pane: The area on the left or top that contains navigation items.
  • Header: An area at the top of the content area, often used for a page title or a BreadcrumbBar.
  • Content Area: The main area of the control where page content is displayed.
  • AutoSuggestBox: An optional search box integrated into the navigation pane.
  • MenuItems: The primary list of navigation items.
  • FooterMenuItems: A secondary list of navigation items, typically for settings or about pages.

Basic Usage

Define NavigationView in your XAML and add NavigationViewItem objects to the MenuItems and FooterMenuItems collections.

xml
<ui:NavigationView
    xmlns:pages="clr-namespace:YourApp.Views.Pages"
    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
    <ui:NavigationView.MenuItems>
        <ui:NavigationViewItem
            Content="Home"
            Icon="{ui:SymbolIcon Home24}"
            TargetPageType="{x:Type pages:DashboardPage}" />
        <ui:NavigationViewItem
            Content="Data"
            Icon="{ui:SymbolIcon DataHistogram24}"
            TargetPageType="{x:Type pages:DataPage}" />
    </ui:NavigationView.MenuItems>
    <ui:NavigationView.FooterMenuItems>
        <ui:NavigationViewItem
            Content="Settings"
            Icon="{ui:SymbolIcon Settings24}"
            TargetPageType="{x:Type pages:SettingsPage}" />
    </ui:NavigationView.FooterMenuItems>
</ui:NavigationView>

[!NOTE]
TargetPageType is a required property on NavigationViewItem that specifies the page to navigate to when the item is selected. The value must be a System.Type.

Programmatic Navigation

You can navigate programmatically by calling the Navigate method with either the Type of the page or its PageTag.

csharp
// Navigate by Type
MyNavigationView.Navigate(typeof(SettingsPage));

// Navigate by Tag
MyNavigationView.Navigate("settings");

To use tags, you must define a PageTag on the NavigationViewItem. If not defined, a tag is automatically generated from the Content property (e.g., "Settings Page" becomes "settingspage").

xml
<ui:NavigationViewItem
    Content="Settings"
    PageTag="settings"
    TargetPageType="{x:Type pages:SettingsPage}" />

Back Navigation

NavigationView automatically handles back navigation. The back button is shown when CanGoBack is true. You can also call GoBack() programmatically.

csharp
if (MyNavigationView.CanGoBack)
{
    MyNavigationView.GoBack();
}

Pane Display Mode

Control the visibility and behavior of the navigation pane with the PaneDisplayMode property.

  • Left: The pane is always open on the left.
  • Top: The pane is shown as a horizontal bar at the top.
  • LeftCompact: The pane is collapsed to show only icons, and expands on hover or when the hamburger button is clicked.
  • LeftMinimal: The pane is hidden and can be opened as an overlay.
xml
<ui:NavigationView PaneDisplayMode="Top" />

You can also control the pane's open state with the IsPaneOpen property.

[!TIP]
To create a responsive layout that changes PaneDisplayMode based on window width, bind PaneDisplayMode to a property in your ViewModel and update it in the Window.SizeChanged event.

The Header property provides a content area above the navigation frame. It is commonly used with a BreadcrumbBar to show the user's location.

xml
<ui:NavigationView>
    <ui:NavigationView.Header>
        <ui:BreadcrumbBar />
    </ui:NavigationView.Header>
</ui:NavigationView>

The BreadcrumbBar will automatically sync with the NavigationView's navigation history.

MVVM Integration

For MVVM applications, it is recommended to use INavigationService and IPageService for navigation and page resolution.

1. Service Configuration

First, register the required services and your pages/ViewModels with your dependency injection container.

csharp
// Using Microsoft.Extensions.DependencyInjection
Host.CreateDefaultBuilder()
    .ConfigureServices((context, services) =>
    {
        // Main window
        services.AddScoped<IWindow, MainWindow>();
        services.AddScoped<MainWindowViewModel>();

        // Services
        services.AddSingleton<INavigationService, NavigationService>();
        services.AddSingleton<IPageService, PageService>();

        // Pages and ViewModels
        services.AddScoped<DashboardPage>();
        services.AddScoped<DashboardViewModel>();
        services.AddScoped<SettingsPage>();
        services.AddScoped<SettingsViewModel>();
    }).Build();

2. ViewModel Setup

In your MainWindowViewModel, define collections for your navigation items and bind them to the NavigationView.

csharp
public partial class MainWindowViewModel : ObservableObject
{
    [ObservableProperty]
    private ICollection<object> _menuItems = new ObservableCollection<object>();

    [ObservableProperty]
    private ICollection<object> _footerMenuItems = new ObservableCollection<object>();

    public MainWindowViewModel()
    {
        MenuItems = new ObservableCollection<object>
        {
            new NavigationViewItem("Home", SymbolRegular.Home24, typeof(DashboardPage)),
            new NavigationViewItem("Data", SymbolRegular.DataHistogram24, typeof(DataPage))
        };

        FooterMenuItems = new ObservableCollection<object>
        {
            new NavigationViewItem("Settings", SymbolRegular.Settings24, typeof(SettingsPage))
        };
    }
}

3. View Setup

In your MainWindow.xaml, bind the MenuItemsSource and FooterMenuItemsSource properties to the collections in your ViewModel. Then, attach the INavigationService.

xml
<ui:NavigationView
    x:Name="RootNavigationView"
    MenuItemsSource="{Binding MenuItems}"
    FooterMenuItemsSource="{Binding FooterMenuItems}" />
csharp
public partial class MainWindow : IWindow
{
    public MainWindow(
        MainWindowViewModel viewModel,
        INavigationService navigationService,
        IPageService pageService
    )
    {
        ViewModel = viewModel;
        DataContext = this;
        InitializeComponent();

        // Attach the service to the NavigationView
        navigationService.SetNavigationControl(RootNavigationView);
        
        // You can also set the page service, which is required for some functionalities
        RootNavigationView.SetPageService(pageService);
    }

    public MainWindowViewModel ViewModel { get; }
}

4. Navigating from a ViewModel

Inject INavigationService into any ViewModel and use it to navigate.

csharp
public partial class DashboardViewModel : ObservableObject
{
    private readonly INavigationService _navigationService;

    public DashboardViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
    }

    [RelayCommand]
    private void OnGoToSettings()
    {
        _navigationService.Navigate(typeof(SettingsPage));
    }
}

NavigationView provides several events to hook into the navigation lifecycle:

  • Navigating: Occurs before navigation starts. Can be cancelled.
  • Navigated: Occurs after navigation is complete.
  • SelectionChanged: Occurs when a NavigationViewItem is selected.
csharp
private void OnNavigating(NavigationView sender, NavigatingCancelEventArgs args)
{
    // Don't navigate to settings if the user is not an admin
    if (args.PageType == typeof(SettingsPage) && !_isAdmin)
    {
        args.Cancel = true;
    }
}

Implement INavigationAware on your page's code-behind or INavigableView<T> on your ViewModel to receive navigation events directly.

INavigationAware

This interface is ideal for code-behind scenarios.

csharp
public partial class MyPage : INavigationAware
{
    public void OnNavigatedTo()
    {
        // Page was navigated to
    }

    public void OnNavigatedFrom()
    {
        // Page was navigated away from
    }
}

`INavigableView`

This interface is designed for MVVM. Your page must inherit from INavigableView<T> where T is its ViewModel. The ViewModel will then receive the navigation calls.

Page:

csharp
[GalleryPage("My Page", SymbolRegular.Page24)]
public partial class MyPage : INavigableView<MyViewModel>
{
    public MyViewModel ViewModel { get; }

    public MyPage(MyViewModel viewModel)
    {
        ViewModel = viewModel;
        DataContext = this;
        InitializeComponent();
    }
}

ViewModel:

csharp
public partial class MyViewModel : ObservableObject, INavigationAware
{
    public void OnNavigatedTo()
    {
        // Page was navigated to
    }

    public void OnNavigatedFrom()
    {
        // Page was navigated away from
    }
}

[!IMPORTANT]
For INavigableView<T> to work, your page must have a public ViewModel property that returns an instance of the ViewModel.

History and Caching

NavigationView maintains a navigation history.

  • History: A collection of Page instances that have been visited.
  • CacheHistory: The number of pages to keep in memory. The default is 0. Set to a value greater than 0 to cache pages. When a cached page is navigated to, its previous state is preserved.
xml
<ui:NavigationView CacheHistory="5" />

[!CAUTION]
Caching pages increases memory consumption. Use it only for pages that are expensive to create or where preserving state is critical. Avoid caching pages that display frequently changing data.


Documentation Nuget (`docs/documentation/nuget.md`)

NuGet package for WPF UI

What's NuGet?

NuGet is a free, open-source package management system for the Microsoft .NET platform. It simplifies the process of finding, installing, and managing third-party libraries and tools in .NET projects. NuGet allows developers to easily add functionality to their projects without having to manually download and reference external libraries. It also provides a way for developers to publish and share their own packages with the .NET community. Overall, NuGet makes it easier to manage dependencies in .NET projects and helps to improve productivity for developers.

How to install **WPF UI** in Visual Studio using the NuGet Package Manager?

1. Create new WPF project

2. Open _Manage NuGet Packages_ window via solution explorer

3. In the _Browse_ tab, enter "**WPF-UI**"

4. Install **WPF-UI** package

Done!

Package installed, to learn more, go to the Getting started page.


Documentation Releases (`docs/documentation/releases.md`)

WPF UI Releases

Version Is supported
3.0.0 Yes

Documentation Symbolicon (`docs/documentation/symbolicon.md`)

SymbolIcon

SymbolIcon is a control responsible for rendering icons.

Implementation

csharp
class Wpf.Ui.Controls.SymbolIcon

Exposes

csharp
// Gets or sets displayed symbol
SymbolIcon.Symbol = SymbolRegular.Empty;
csharp
// Defines whether or not we should use the SymbolFilled
SymbolIcon.Filled = false;
csharp
// Icon foreground
SymbolIcon.Foreground = Brushes.White;
csharp
// Icon size
SymbolIcon.FontSize = 16;

How to use

xml
<ui:SymbolIcon
  Symbol="Fluent24"
  Filled="False"
  FontSize="16"
  Foreground="White"/>

Documentation System Theme Watcher (`docs/documentation/system-theme-watcher.md`)

SystemThemeWatcher

SystemThemeWatcher automatically synchronizes the application's theme, accent color, and window backdrop with the current Windows theme settings. It listens for system-level changes and applies them to your application in real-time.

[!TIP]
The simplest way to enable system theme tracking is to call SystemThemeWatcher.Watch(this); in your main window's constructor.

How It Works

SystemThemeWatcher attaches a hook to a window's message procedure (WndProc) and listens for the WM_WININICHANGE system message. When Windows broadcasts this message (e.g., when the user changes from light to dark mode), the watcher automatically calls ApplicationThemeManager.ApplySystemTheme() to update your application's appearance.

Basic Usage

Enable theme watching in your window's constructor. This will use the default Mica backdrop and update accent colors.

csharp
using Wpf.Ui.Appearance;

public partial class MainWindow : System.Windows.Window
{
    public MainWindow()
    {
        // This will apply the system theme, accent, and default backdrop.
        SystemThemeWatcher.Watch(this);

        InitializeComponent();
    }
}

Customization

You can customize the backdrop effect and control whether the accent color is updated.

Window Backdrop

Specify the WindowBackdropType to apply when the theme changes.

csharp
SystemThemeWatcher.Watch(
    this,
    WindowBackdropType.Acrylic // Use Acrylic backdrop
);

Available backdrop types:

  • None: No backdrop effect.
  • Auto: Automatically selects the appropriate backdrop.
  • Mica: The default Windows 11 Mica effect.
  • Acrylic: The semi-transparent Acrylic effect.
  • Tabbed: A blurred wallpaper effect available in recent Windows 11 versions.

Accent Color Updates

You can prevent the watcher from changing the application's accent color.

csharp
SystemThemeWatcher.Watch(
    this,
    updateAccents: false // Theme will change, but accent color will not
);

Stop Watching

To stop a window from responding to system theme changes, use the UnWatch method.

csharp
SystemThemeWatcher.UnWatch(this);

[!IMPORTANT]
Do not call UnWatch on a window that has not been loaded, as it will throw an InvalidOperationException. It is safe to call Watch on a window that is not yet loaded.

Dependency Injection Usage

If you are resolving your main window from a dependency injection container, you can start the watcher after the host is built.

csharp
var host = Host.CreateDefaultBuilder()
    .ConfigureServices(services =>
    {
        services.AddHostedService<ApplicationHostService>();
        services.AddSingleton<MainWindow>();
        // ... other services
    }).Build();

await host.StartAsync();

var mainWindow = host.Services.GetRequiredService<MainWindow>();

// Watch the window after it's been created
SystemThemeWatcher.Watch(mainWindow);

[!NOTE]
SystemThemeWatcher works on a static, global basis. While you can Watch multiple windows, the theme and accent settings applied will be the same for all of them based on the parameters of the last Watch call that triggered an update.


Documentation Themes (`docs/documentation/themes.md`)

Application Themes

WPF UI provides a robust theming system that allows you to control your application's appearance, including support for light, dark, and high contrast modes. The ApplicationThemeManager class is the primary tool for managing themes at runtime.

[!IMPORTANT]
For theme changes to apply correctly, your colors and brushes should be referenced as DynamicResource.

Setting the Initial Theme

The easiest way to set the initial theme is by using the ThemesDictionary in your App.xaml. This ensures that the correct theme resources are loaded at startup.

xml
<Application
    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ui:ThemesDictionary Theme="Light" />
                <ui:ControlsDictionary />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

The Theme property on ThemesDictionary can be set to Light or Dark.

Changing the Theme at Runtime

Use the ApplicationThemeManager.Apply() method to change the theme while the application is running.

csharp
using Wpf.Ui.Appearance;
using Wpf.Ui.Controls;

// Apply the Light theme with a Mica backdrop
ApplicationThemeManager.Apply(
    ApplicationTheme.Light,
    WindowBackdropType.Mica
);

`ApplicationTheme` Enum

This enum specifies the theme to apply:

  • Light: The standard light theme.
  • Dark: The standard dark theme.
  • HighContrast: Automatically selects the appropriate Windows High Contrast theme.

System Theme Integration

You can synchronize your application's theme with the current Windows theme settings.

One-Time Sync

To apply the current system theme once, use ApplySystemTheme().

csharp
// Apply the current Windows theme (light or dark)
ApplicationThemeManager.ApplySystemTheme();

Automatic Tracking

For continuous synchronization, use the SystemThemeWatcher. It automatically updates your app's theme and accent color when the user changes their Windows settings. See the SystemThemeWatcher documentation for more details.

csharp
using Wpf.Ui.Appearance;

public partial class MainWindow : System.Windows.Window
{
    public MainWindow()
    {
        // Watch for system theme changes
        SystemThemeWatcher.Watch(this);

        InitializeComponent();
    }
}

Reading the Current Theme

You can get the current application and system themes at any time.

  • ApplicationThemeManager.GetAppTheme(): Returns the current ApplicationTheme (Light, Dark, or HighContrast).
  • ApplicationThemeManager.GetSystemTheme(): Returns the current SystemTheme.
csharp
ApplicationTheme currentAppTheme = ApplicationThemeManager.GetAppTheme();
SystemTheme currentSystemTheme = ApplicationThemeManager.GetSystemTheme();

if (currentAppTheme == ApplicationTheme.Dark)
{
    // ...
}

`SystemTheme` Enum

This enum represents the actual theme reported by Windows, including decorative themes like Glow, CapturedMotion, and Sunrise. ApplicationThemeManager maps these to either Light or Dark.

High Contrast Themes

WPF UI automatically handles Windows High Contrast themes. When a high contrast mode is detected, ApplicationThemeManager loads the appropriate high contrast resource dictionary (HC1, HC2, HCBlack, or HCWhite).

  • ApplicationThemeManager.IsHighContrast(): Checks if the application is currently in a high contrast theme.
  • ApplicationThemeManager.IsSystemHighContrast(): Checks if Windows is currently in a high contrast mode.

Theme Changed Event

The ApplicationThemeManager.Changed event is triggered whenever the application's theme is successfully changed.

csharp
ApplicationThemeManager.Changed += (currentTheme, currentAccent) =>
{
    Debug.WriteLine($"Theme changed to {currentTheme} with accent {currentAccent}");
};

This event is useful for applying custom logic after a theme change, such as updating graphics or non-WPF UI elements.

[!TIP]
The Changed event is fired by both manual Apply() calls and automatic updates from SystemThemeWatcher, providing a single place to react to any theme change.


Migration V2 Migration (`docs/migration/v2-migration.md`)

Migration plan

This page outlines key changes and important details to consider when migrating. It highlights what’s new, what’s changed, and any steps you need to take to ensure a smooth transition. This isn’t a full step-by-step guide but a quick reference to help you navigate the most critical parts of the migration process.

Key changes in v2

Global namespace change

In version 2.0 the namespace has been changed from WPF-UI to the more .NET compatible Wpf.Ui. The package name has not been changed to maintain branding and consistency.

The navigation control has been rewritten yet again, making it almost completely incompatible.

All navigation controls inherit from Wpf.Ui.Controls.Navigation.NavigationBase base class.

xml
<ui:NavigationStore
  Frame="{Binding ElementName=RootFrame}"
  Precache="False"
  SelectedPageIndex="-1"
  TransitionDuration="200"
  TransitionType="FadeInWithSlide">
  <ui:NavigationStore.Items>
    <ui:NavigationItem
      Cache="True"
      Content="Home"
      Icon="Home24"
      PageTag="dashboard"
      PageType="{x:Type pages:Dashboard}" />
    <ui:NavigationSeparator />
  </ui:NavigationStore.Items>
  <ui:NavigationStore.Footer>
    <ui:NavigationItem
      Click="NavigationButtonTheme_OnClick"
      Content="Theme"
      Icon="DarkTheme24" />
  </ui:NavigationStore.Footer>
</ui:NavigationStore>
xml
<ui:NavigationFluent
  Frame="{Binding ElementName=RootFrame}"
  Precache="False"
  SelectedPageIndex="-1"
  TransitionDuration="200"
  TransitionType="FadeInWithSlide">
  <ui:NavigationFluent.Items>
    <ui:NavigationItem
      Cache="True"
      Content="Home"
      Icon="Home24"
      PageTag="dashboard"
      PageType="{x:Type pages:Dashboard}" />
    <ui:NavigationSeparator />
  </ui:NavigationFluent.Items>
  <ui:NavigationFluent.Footer>
    <ui:NavigationItem
      Click="NavigationButtonTheme_OnClick"
      Content="Theme"
      Icon="DarkTheme24" />
  </ui:NavigationFluent.Footer>
</ui:NavigationFluent>
xml
<ui:NavigationCompact
  Frame="{Binding ElementName=RootFrame}"
  Precache="False"
  SelectedPageIndex="-1"
  TransitionDuration="200"
  TransitionType="FadeInWithSlide">
  <ui:NavigationCompact.Items>
    <ui:NavigationItem
      Cache="True"
      Content="Home"
      Icon="Home24"
      PageTag="dashboard"
      PageType="{x:Type pages:Dashboard}" />
    <ui:NavigationSeparator />
  </ui:NavigationCompact.Items>
  <ui:NavigationCompact.Footer>
    <ui:NavigationItem
      Click="NavigationButtonTheme_OnClick"
      Content="Theme"
      Icon="DarkTheme24" />
  </ui:NavigationCompact.Footer>
</ui:NavigationCompact>

Titlebar

The titlebar control has been rewritten yet again, making it almost completely incompatible.


Migration V3 Migration (`docs/migration/v3-migration.md`)

Migration plan

This page outlines key changes and important details to consider when migrating. It highlights what’s new, what’s changed, and any steps you need to take to ensure a smooth transition. This isn’t a full step-by-step guide but a quick reference to help you navigate the most critical parts of the migration process.

All navigation controls have been merged into one NavigationView, inspired by Win UI.

Icons

All icons are based on the new IconElement control. They replaced icons in TitleBar, NavigationView, Button and other controls.

Inspired by Win UI Controls Gallery, a new application for testing and browsing controls was created -** WPF UI Gallery**. It replaced the Demo app.

Dialogs

ContentDialog, MessageBox and Snackbar have been modified, their interfaces are not fully compatible.


Migration V4 Migration (`docs/migration/v4-migration.md`)

Migration plan

This page outlines key changes and important details to consider when migrating. It highlights what’s new, what’s changed, and any steps you need to take to ensure a smooth transition. This isn’t a full step-by-step guide but a quick reference to help you navigate the most critical parts of the migration process.

Abstractions package

Some WPF UI interfaces have been moved to a standalone package WPF-UI.Abstractions. You don't need to reference it, it will always be added automatically with WPF-UI NuGet package.

Navigation interfaces have been moved to a standalone WPF-UI.Abstractions package. This way, if you have models, views or other business services in another project, not related to WPF, you can develop them together for multiple applications.

New namespaces

INavigationAware and INavigableView have beed moved to Wpf.Ui.Abstractions.Controls namespace.

Dependency injection based page creation

IPageService have been renamed to INavigationViewPageProvider.

Its default implementation is in the new Wpf.Ui.DependencyInjection package. You just need to use the services.AddNavigationViewPageProvider() extension and then indicate in the navigation that you want to use this interface. Then NavigationView will use DI container for pages creation.

Program.cs

csharp
var builder = Host.CreateDefaultBuilder();
builder.Services.AddNavigationViewPageProvider();

MyWindow.xaml.cs

csharp
var pageProvider = serviceProvider.GetRequiredService<INavigationViewPageProvider>();

// NavigationControl is x:Name of our NavigationView defined in XAML.
NavigationControl.SetPageProviderService(pageProvider)

The INavigationService defined in the main package (WPF-UI) makes navigation management easy. You can use it for convenient injection between view models. We HIGHLY recommend it to be Singleton.

csharp
var builder = Host.CreateDefaultBuilder();
builder.Services.AddNavigationViewPageProvider();
builder.Services.AddSingleton<INavigationService, NavigationService>();

Templates .Stylelintrc.Json (`docs/templates/.stylelintrc.json`)

{
"extends": "stylelint-config-standard-scss",
"ignoreFiles": [
"**/*.ts"
],
"rules": {
"selector-class-pattern": null,
"font-family-no-missing-generic-family-keyword": [ true, {
"ignoreFontFamilies": [
"bootstrap-icons"
]
}]
}
}


Templates Package.Json (`docs/templates/package.json`)

{
"name": "@docfx/template",
"version": "1.0.0",
"description": "Docfx static website templates",
"keywords": [
"docfx",
"template"
],
"author": "docfx",
"license": "MIT",
"browserslist": [
"defaults"
],
"scripts": {
"build": "node build.js",
"lint": "eslint wpfui/src && stylelint wpfui/src"
},
"dependencies": {
"@default/anchor-js": "npm:[email protected]",
"@default/bootstrap": "npm:[email protected]",
"@default/highlight.js": "npm:[email protected]",
"@default/lunr": "npm:[email protected]",
"@default/mark.js": "npm:[email protected]",
"@default/twbs-pagination": "josecebe/twbs-pagination#1.3.1",
"@default/url": "npm:@websanova/[email protected]",
"@websanova/url": "^2.6.3",
"anchor-js": "^5.0.0",
"bootstrap": "^5.3.1",
"bootstrap-icons": "^1.10.5",
"highlight.js": "^11.8.0",
"jquery": "3.7.0",
"lit-html": "^2.8.0",
"lunr": "2.3.9",
"mathjax": "^3.2.2",
"mermaid": "^10.3.0"
},
"devDependencies": {
"@types/jest": "^29.5.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"browser-sync": "^2.29.3",
"esbuild": "~0.18.17",
"esbuild-sass-plugin": "~2.10.0",
"eslint": "^8.46.0",
"eslint-config-standard": "^17.1.0",
"jest": "^29.6.2",
"stylelint": "^15.10.2",
"stylelint-config-standard-scss": "^10.0.0",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6",
"yargs": "^17.7.2"
}
}


Templates Tsconfig.Json (`docs/templates/tsconfig.json`)

{
"compilerOptions": {
"removeComments": true,
"resolveJsonModule": true,
"esModuleInterop": true
}
}


Architecture Cross Cutting Navigation (`docs/architecture/cross-cutting/navigation.md`)

WPF UI v4.2.0 | Cross-Cutting Concern

Overview

The WPF UI navigation system provides page-based navigation within NavigationView, with support for page caching, back stack management, transition animations, and lifecycle callbacks. It integrates with Microsoft.Extensions.DependencyInjection for type-based page resolution.


The following sequence diagram shows the complete flow from a NavigationService.Navigate() call through to the page being displayed with transition animations.

text
/* Detailed source-code truncated for AI context efficiency. */

Page Cache Mode

NavigationView supports three caching strategies via the NavigationCacheMode property on individual pages. The cache is maintained per-type within the INavigationViewPageProvider implementation.

mermaid
stateDiagram-v2
    [*] --> PageRequested: Navigate(pageType)

    state PageRequested {
        [*] --> CheckCacheMode

        state CheckCacheMode <<choice>>
        CheckCacheMode --> CacheDisabled: Disabled
        CheckCacheMode --> CacheEnabled: Enabled
        CheckCacheMode --> CacheRequired: Required

        state CacheDisabled {
            [*] --> CreateNew_D: Always create new instance
            CreateNew_D --> ReturnPage_D: Return new page
        }

        state CacheEnabled {
            [*] --> LookupCache_E: Check cache
            LookupCache_E --> ReturnCached_E: Cache hit
            LookupCache_E --> CreateAndCache_E: Cache miss
            CreateAndCache_E --> ReturnPage_E: Store and return
            ReturnCached_E --> ReturnPage_E: Return cached
        }

        state CacheRequired {
            [*] --> LookupCache_R: Check cache
            LookupCache_R --> ReturnCached_R: Cache hit (guaranteed after first)
            LookupCache_R --> CreateAndCache_R: First request only
            CreateAndCache_R --> ReturnPage_R: Store and return
            ReturnCached_R --> ReturnPage_R: Return cached
        }
    }

    PageRequested --> PageDisplayed: Page resolved
    PageDisplayed --> [*]

Cache Mode Comparison

Mode First Visit Subsequent Visits Page State Use Case
Disabled New instance New instance Lost on navigate away Forms, transient views
Enabled New instance Cached instance (if available) Preserved while cached Dashboard, lists
Required New instance Always cached instance Always preserved Settings, stateful views

Key Components

Thin wrapper around INavigationView that provides a service-oriented API for navigation. Registered in DI as INavigationService.

Key methods:

  • Navigate(Type pageType) — Navigate to a page by type
  • Navigate(string pageTag) — Navigate to a page by tag
  • GoBack() — Navigate to the previous page in the back stack
  • SetNavigationControl(INavigationView) — Bind to a NavigationView instance

INavigationViewPageProvider

Abstraction for page instance resolution. Two implementations:

  1. DependencyInjectionNavigationViewPageProvider (from Wpf.Ui.DependencyInjection) — resolves pages via IServiceProvider
  2. Manual/custom — consumers can implement their own provider

INavigationAware

Lifecycle interface for pages that need to respond to navigation events:

  • OnNavigatedTo() — Called when the page becomes the active view
  • OnNavigatedFrom() — Called when the page is navigated away from

Transition Animations

TransitionAnimationProvider applies entry animations to navigated pages. Available transitions: FadeIn, FadeInFromBottom, SlideFromBottom, SlideFromRight, SlideFromLeft. Animations are skipped when HardwareAcceleration.RenderingTier < 2.


Integration with DI

For hosted applications using Microsoft.Extensions.Hosting:

csharp
// In Program.cs or Startup
services.AddNavigationViewPageProvider<DependencyInjectionNavigationViewPageProvider>();
services.AddSingleton<INavigationService, NavigationService>();

// Pages registered in DI
services.AddTransient<DashboardPage>();
services.AddTransient<SettingsPage>();

The DependencyInjectionNavigationViewPageProvider resolves pages from the DI container, respecting their registered lifetime (Transient, Scoped, Singleton).


Back Stack

NavigationView maintains an internal back stack of previously visited page types. The GoBack() operation pops the most recent entry and navigates to it. The back stack is cleared when navigating to a page that is already in the stack (cycle prevention).

Design Considerations

  • Static vs DI navigation: Simple apps can use NavigationService directly; hosted apps use DI. Both paths are supported.
  • Cache ownership: The page cache lives in INavigationViewPageProvider, not in NavigationView. This allows DI-managed lifetimes to control cache behavior.
  • Animation gating: Transition animations check rendering tier to avoid jank on software-rendered systems.
  • Thread safety: Navigation must occur on the UI thread. NavigationService does not marshal calls.

Architecture Cross Cutting Testing (`docs/architecture/cross-cutting/testing.md`)

Cross-Cutting Concern: Testing

Project: WPF UI (wpfui) v4.2.0
Last Updated: 2026-02-10

Overview

WPF UI employs a two-tier testing strategy consisting of unit tests for isolated logic verification and integration tests for end-to-end UI automation. The testing infrastructure is intentionally lightweight, reflecting the library's nature as a visual control library where many behaviors require a running WPF application to validate.

Test Pyramid

text
/  Integration Tests  \        8 tests
          /  (FlaUI + Gallery App) \      End-to-end UI automation
         /________________________\
        /                          \
       /      Unit Tests            \     6 tests
      /  (XUnit + NSubstitute)       \    Isolated logic, pure functions
     /________________________________\
Layer Scope Framework Assertion Library Count
Unit Tests Pure logic, extension methods, animation providers XUnit 2.9.3 + NSubstitute 5.3.0 Xunit.Assert 6
Integration Tests Window management, navigation, dialogs, title bar XUnit v3 3.2.0 + FlaUI.UIA3 5.0.0 AwesomeAssertions 9.3.0 8

Test Framework Versions

All versions are managed centrally in Directory.Packages.props:

Package Version Purpose
xunit 2.9.3 Unit test framework (v2-style API)
xunit.v3 3.2.0 Integration test framework (v3 with IAsyncLifetime)
xunit.runner.visualstudio 3.1.5 Visual Studio / dotnet test runner
Microsoft.NET.Test.Sdk 18.0.0 .NET test SDK infrastructure
NSubstitute 5.3.0 Mocking library for unit tests
AwesomeAssertions 9.3.0 Fluent assertions (FluentAssertions successor)
FlaUI.Core 5.0.0 UI automation core library
FlaUI.UIA3 5.0.0 UIA3 automation adapter
coverlet.collector 6.0.4 Code coverage collection

Test Naming Conventions

Unit Tests

Pattern: MethodName_ExpectedResult_WhenCondition

csharp
[Fact]
public void ApplyTransition_ReturnsFalse_WhenDurationIsLessThan10()

Alternative pattern: GivenX_Method_ExpectedResult

csharp
[Fact]
public void GivenAllRegularSymbols_Swap_ReturnsValidFilledSymbol()

Integration Tests

Pattern: Subject_ShouldExpectedBehavior_WhenCondition

csharp
[Fact]
public async Task CloseButton_ShouldCloseWindow_WhenClicked()

Class Naming

  • Unit test classes: {ClassUnderTest}Tests (e.g., TransitionAnimationProviderTests)
  • Integration test classes: {Feature}Tests (e.g., TitleBarTests, NavigationTests)
  • Integration test classes are sealed; unit test classes are not

Test Directory Structure

mermaid
graph TD
    A["tests/"] --> B["Wpf.Ui.UnitTests/"]
    A --> C["Wpf.Ui.Gallery.IntegrationTests/"]

    B --> B1["Wpf.Ui.UnitTests.csproj<br/><i>net10.0-windows</i>"]
    B --> B2["Animations/"]
    B --> B3["Extensions/"]
    B --> B4["GlobalUsings.cs"]
    B2 --> B2a["TransitionAnimationProviderTests.cs"]
    B3 --> B3a["SymbolExtensionsTests.cs"]

    C --> C1["Wpf.Ui.Gallery.IntegrationTests.csproj<br/><i>net10.0-windows10.0.26100.0</i>"]
    C --> C2["Fixtures/"]
    C --> C3["WindowTests.cs"]
    C --> C4["TitleBarTests.cs"]
    C --> C5["NavigationTests.cs"]
    C --> C6["ContentDialogAutomationTests.cs"]
    C --> C7["xunit.runner.json"]
    C2 --> C2a["UiTest.cs <i>(base class)</i>"]
    C2 --> C2b["TestedApplication.cs <i>(app lifecycle)</i>"]

    D["src/Wpf.Ui.FlaUI/"] --> D1["AutoSuggestBox.cs<br/><i>Custom FlaUI element</i>"]

    style A fill:#f5f5f5,stroke:#333
    style B fill:#e3f2fd,stroke:#1565c0
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#fff3e0,stroke:#e65100

Test Infrastructure

Unit Tests

Unit tests reference the core Wpf.Ui project directly and use NSubstitute for mocking WPF types (e.g., UIElement). Global usings are defined for common namespaces:

text
System, System.Windows, NSubstitute, Xunit

Source: tests/Wpf.Ui.UnitTests/GlobalUsings.cs

Integration Tests

Integration tests use a custom infrastructure built on FlaUI:

  • TestedApplication (IAsyncLifetime): Launches and manages the Gallery .exe process. Finds the executable in the test output directory. Uses UIA3Automation for UI element discovery.
  • UiTest (abstract base class, IAsyncLifetime): Provides helper methods for all UI tests:
    • FindFirst(string automationId) -- finds UI elements by automation ID
    • FindFirst(Func<ConditionFactory, ConditionBase>) -- finds by condition
    • Wait(int seconds) -- async delay for UI settling
    • Enter(string value) -- simulates keyboard text input
    • Press(VirtualKeyShort) -- simulates a key press

Integration Test Runner Configuration

Tests run sequentially (no parallel test collections) with invariant culture:

json
{
  "parallelizeTestCollections": false,
  "diagnosticMessages": true,
  "culture": "invariant"
}

Source: tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json

Run Commands

bash
# Run unit tests
dotnet test tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj

# Run integration tests (requires built Gallery app)
dotnet test tests/Wpf.Ui.Gallery.IntegrationTests/Wpf.Ui.Gallery.IntegrationTests.csproj

# Run all tests with coverage
dotnet test tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj --collect:"XPlat Code Coverage"

Coverage Gaps and Observations

Area Current Coverage Notes
Animations 2 unit tests TransitionAnimationProvider edge cases only
Extensions 4 unit tests SymbolExtensions.Swap() and GetString() exhaustive enum tests
Controls (77+) 0 unit tests Controls depend on WPF runtime; consider UI automation expansion
Services 0 unit tests INavigationService, IContentDialogService, etc. are testable via mocks
Theming 0 tests Static managers (ApplicationThemeManager) limit testability
Win32 Interop 0 tests Requires OS-level interaction; integration tests more appropriate
Window Chrome 3 integration tests TitleBar close/minimize/maximize buttons
Navigation 2 integration tests AutoSuggestBox search and sidebar navigation
Dialogs 2 integration tests ContentDialog result text and keyboard focus isolation
Window 1 integration test Window title verification

CI/CD Integration

The PR validation workflow (.github/workflows/wpf-ui-pr-validator.yaml) currently only builds the Gallery app in Release mode. It does not run unit or integration tests as part of PR checks. Test execution is a local development responsibility.


Architecture Cross Cutting Theming And Appearance (`docs/architecture/cross-cutting/theming-and-appearance.md`)

Theming and Appearance System

Overview

The WPF UI library implements a comprehensive theming system that supports Light, Dark, and four high-contrast themes. The system automatically synchronizes with OS theme changes, manages accent colors, and provides window backdrop effects (Mica, Acrylic, Tabbed).

Architecture Components

Core Manager Classes

ApplicationThemeManager

Location: src/Wpf.Ui/Appearance/ApplicationThemeManager.cs

Static class responsible for applying and managing application themes. Key functionality:

  • Apply(ApplicationTheme theme) - Swaps theme resource dictionaries at runtime
  • GetAppTheme() - Retrieves current application theme
  • Changed event - ThemeChangedEvent delegate fires when theme changes globally

The manager uses URI-based resource dictionary swapping, searching application-level merged dictionaries for URIs containing 'wpf.ui;' and 'theme', then replacing them with the appropriate theme file.

ApplicationAccentColorManager

Location: src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs

Static class for accent color management:

  • Apply(Color systemAccent, ApplicationTheme theme) - Updates 20+ dynamic color resources
  • GetColorizationColor() - Retrieves system accent color
  • ApplySystemAccent() - Applies Windows system accent colors

Uses WinRT IUISettings3 COM interface to retrieve system accent colors (Accent, AccentLight1-3, AccentDark1-3) with registry fallback to DWM AccentColor.

Dynamic Resources Updated:

  • SystemAccentColor
  • AccentFillColorDefault
  • TextOnAccentFillColorPrimary
  • AccentFillColorSecondary
  • AccentFillColorTertiary
  • (20+ total accent-related resources)

SystemThemeWatcher

Location: src/Wpf.Ui/Appearance/SystemThemeWatcher.cs

Static class providing automatic OS theme synchronization:

  • Watch(Window window) - Hooks window to auto-sync theme with OS
  • UnWatch(Window window) - Removes synchronization hook

Implementation uses WndProc message interception via HwndSource to listen for:

  • WM_DWMCOLORIZATIONCOLORCHANGED
  • WM_THEMECHANGED
  • WM_SYSCOLORCHANGE

When detected, triggers ApplicationThemeManager.ApplySystemTheme().

WindowBackgroundManager

Location: src/Wpf.Ui/Appearance/WindowBackgroundManager.cs

Static class for window appearance management:

  • UpdateBackground(Window? window, ApplicationTheme applicationTheme, WindowBackdropType backdrop) - Applies dark mode and backdrop effects
  • Manages WindowBackdrop effects (Mica, Acrylic, Tabbed)
  • Applies DWM window attributes for Windows 11+ visual effects

ResourceDictionaryManager

Location: src/Wpf.Ui/Appearance/ResourceDictionaryManager.cs

Internal helper class for resource dictionary manipulation:

  • Finds resource dictionaries by namespace/name matching
  • Swaps resource dictionaries by URI pattern
  • Handles Application.Current.Resources.MergedDictionaries traversal

Theme Files

Six XAML resource dictionaries located in src/Wpf.Ui/Resources/Theme/:

  1. Light.xaml - Light theme color scheme
  2. Dark.xaml - Dark theme color scheme
  3. HC1.xaml - High contrast theme variant 1
  4. HC2.xaml - High contrast theme variant 2
  5. HCBlack.xaml - High contrast black theme
  6. HCWhite.xaml - High contrast white theme

Supporting Resources

Located in src/Wpf.Ui/Resources/ (parent directory, not the Theme/ subdirectory):

  • Accent.xaml - Accent color definitions
  • Palette.xaml - Color palette system
  • StaticColors.xaml - Static color values
  • Variables.xaml - Theme variables

Accent Color System

The accent color system provides dynamic, theme-aware colors derived from Windows system settings:

Color Hierarchy

text
System Accent Color (from WinRT UISettings)
  ├── Primary Accent (direct system color)
  ├── Secondary Accent (lighter/darker variant)
  └── Tertiary Accent (additional variant)

WinRT Integration

csharp
// Retrieves colors via WinRT IUISettings3 COM interface
var uiSettings = new Windows.UI.ViewManagement.UISettings();
var accent = uiSettings.GetColorValue(UIColorType.Accent);
var accentLight1 = uiSettings.GetColorValue(UIColorType.AccentLight1);
var accentDark1 = uiSettings.GetColorValue(UIColorType.AccentDark1);

WindowBackdrop Effects

Three backdrop effect types available via WindowBackdropType enum:

Mica

Windows 11+ translucent backdrop with desktop wallpaper bleed-through. Applied via DwmSetWindowAttribute with DWMWA_SYSTEMBACKDROP_TYPE.

Acrylic

Translucent acrylic material effect with blur. Requires Windows 10 Fall Creators Update or later.

Tabbed

Windows 11 tabbed window effect grouping windows in the taskbar.

Implementation

Effects are applied through DWM (Desktop Window Manager) APIs in WindowBackgroundManager and consumed by FluentWindow control.

Theme Change Flow

mermaid
sequenceDiagram
    participant User
    participant App
    participant SystemThemeWatcher
    participant WndProc
    participant ApplicationThemeManager
    participant ResourceDictionaryManager
    participant UI

    User->>App: Change OS Theme
    WndProc->>SystemThemeWatcher: WM_THEMECHANGED
    SystemThemeWatcher->>ApplicationThemeManager: ApplySystemTheme()
    ApplicationThemeManager->>ApplicationThemeManager: GetSystemTheme()
    ApplicationThemeManager->>ResourceDictionaryManager: UpdateDictionary("theme", newUri)
    ResourceDictionaryManager->>UI: Swap Theme XAML
    ApplicationThemeManager->>ApplicationThemeManager: Fire Changed Event
    ApplicationThemeManager->>UI: Trigger Visual Update
    UI->>User: Updated Appearance

Usage Examples

Basic Theme Application

csharp
// Apply dark theme
ApplicationThemeManager.Apply(ApplicationTheme.Dark);

// Get current theme
ApplicationTheme current = ApplicationThemeManager.GetAppTheme();

Automatic OS Synchronization

csharp
public MainWindow()
{
    InitializeComponent();

    // Enable automatic theme synchronization
    Appearance.SystemThemeWatcher.Watch(this);
}

Custom Accent Color

csharp
// Apply custom accent color
Color myAccent = Color.FromRgb(0, 120, 215);
ApplicationAccentColorManager.Apply(
    myAccent,
    ApplicationTheme.Dark,
    systemGlassColor: false,
    systemAccentColor: true
);

XAML Theme Selection

xml
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            
            <ui:ThemesDictionary Theme="Dark" />
            <ui:ControlsDictionary />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Design Considerations

Static Singleton Pattern

The theme managers use static class design for simple, globally-accessible APIs. This pattern trades testability for API simplicity and ensures single-instance theme state across the application.

Runtime Resource Swapping

Theme changes occur via runtime resource dictionary replacement rather than restart-required configuration. This enables live theme switching without application restart.

OS Integration

Deep integration with Windows theme system via WndProc message hooks and WinRT UISettings ensures automatic synchronization with user preferences.

Multi-Version Support

Theme system gracefully degrades on older Windows versions, falling back to registry-based accent color detection when WinRT APIs are unavailable.


Architecture Cross Cutting Win32 Interop (`docs/architecture/cross-cutting/win32-interop.md`)

Win32 Interop Architecture

WPF UI v4.2.0 | Cross-Cutting Concern

Overview

WPF UI relies heavily on Win32 interop to deliver Fluent Design features that are not natively available through the WPF framework. This includes DWM backdrop effects (Mica, Acrylic, Tabbed), dark mode title bars, window corner preferences, snap layout support, system tray icons, and taskbar progress indicators.

The interop layer follows a strict three-layer architecture that isolates raw platform calls from the rest of the library.


Component Diagram

text
/* Detailed source-code truncated for AI context efficiency. */

Three-Layer Architecture

Layer 1: CsWin32 Source Generation

The project uses Microsoft's CsWin32 source generator to produce type-safe P/Invoke bindings at compile time.

  • Configuration: NativeMethods.txt lists the Win32 functions and types needed by the library.
  • Generated namespace: Windows.Win32
  • Foundation types: HWND, HRESULT, BOOL from Windows.Win32.Foundation

Key generated functions:

Function Purpose
DwmSetWindowAttribute Apply backdrop effects, dark mode, corner preferences
DwmIsCompositionEnabled Check if DWM composition is active
SetWindowLong / GetWindowLong Manipulate window styles (32-bit)
Shell_NotifyIcon System tray icon management
ITaskbarList4 Taskbar progress overlay (COM interface)

Layer 2: Managed Wrappers (`src/Wpf.Ui/Interop/`)

Managed wrappers provide validated, exception-safe access to native APIs.

`UnsafeNativeMethods.cs` -- Handle-Validated Wrappers

Every method follows a defensive pattern:

  1. Check that the handle is not IntPtr.Zero
  2. Verify the handle via PInvoke.IsWindow()
  3. Call the native API
  4. Catch any exception and return false or null

This pattern ensures that callers never receive unmanaged exceptions and that invalid window handles are rejected before reaching the OS.

`UnsafeReflection.cs` -- Unsafe Enum/Struct Casting

Provides unsafe casting between managed enums/structs and their Win32 equivalents. Used where direct marshalling is insufficient or where performance-critical paths avoid boxing.

`PInvoke.cs` -- Custom P/Invoke Declarations

Contains hand-written P/Invoke declarations for functions that CsWin32 does not generate or generates with incompatible signatures. Example: SetWindowLongPtrW requires platform-specific handling (different entry points on 32-bit vs 64-bit Windows).

Layer 3: Utilities (`src/Wpf.Ui/Win32/`)

`Utilities.cs` -- OS Version Detection

Provides high-level queries about the running environment:

Property Logic
IsOSWindows11OrNewer OS build number >= 22000
IsCompositionEnabled Calls DwmIsCompositionEnabled

These checks gate feature availability so that controls degrade gracefully on older Windows versions.


API Surface by Windows Component

API Usage Key Functions
DWM Backdrop effects (Mica/Acrylic/Tabbed), dark mode, corner preferences DwmSetWindowAttribute, DwmIsCompositionEnabled
User32 Window style manipulation, message pump interception, snap layouts SetWindowLong, GetWindowLong, SetWindowLongPtr
Shell32 System tray icons, taskbar progress Shell_NotifyIcon, ITaskbarList4 COM
WinRT UISettings System accent colors (8-color palette) IUISettings3 COM interface
Registry Fallback for accent colors, OS version detection DWM\AccentColor, CurrentVersion
WndProc Theme change detection, title bar hit testing WM_THEMECHANGED, WM_DWMCOLORIZATIONCOLORCHANGED, WM_NCHITTEST

Interop Call Flow

The following diagram illustrates the typical call path from a consumer application through the interop layers to the Windows OS.

mermaid
sequenceDiagram
    participant App as Consumer App
    participant Control as WPF UI Control
    participant Unsafe as UnsafeNativeMethods
    participant CsWin32 as CsWin32 PInvoke
    participant OS as Windows OS

    App->>Control: Set property (e.g., WindowBackdropType = Mica)
    Control->>Control: Validate state and OS version
    Control->>Unsafe: Call managed wrapper (e.g., ApplyWindowDarkMode)

    Unsafe->>Unsafe: Check handle != IntPtr.Zero
    Unsafe->>CsWin32: PInvoke.IsWindow(hwnd)
    CsWin32->>OS: IsWindow()
    OS-->>CsWin32: BOOL result
    CsWin32-->>Unsafe: true/false

    alt Handle is valid
        Unsafe->>CsWin32: DwmSetWindowAttribute(hwnd, attr, value)
        CsWin32->>OS: DwmSetWindowAttribute()
        OS-->>CsWin32: HRESULT
        CsWin32-->>Unsafe: success/failure
        Unsafe-->>Control: true
    else Handle is invalid or call fails
        Unsafe-->>Control: false (exception swallowed)
    end

    Control-->>App: Property applied (or silently degraded)

Error Handling Pattern

The Win32 interop layer follows a deliberate error-swallowing strategy:

  1. Bare catch blocks are intentional. Win32 APIs may fail unpredictably across OS versions, and there is no reliable way to enumerate all failure modes at compile time. Swallowing exceptions ensures the application continues to function, albeit without the requested visual effect.

  2. Graceful degradation is the design goal. If a Mica backdrop cannot be applied (e.g., on Windows 10), the window falls back to a solid background. No exception propagates to the consumer.

  3. Handle validation is mandatory. Every wrapper method must validate that the HWND is non-zero and represents a valid window before calling any native API. This prevents access violations from stale or recycled handles.

  4. Return values signal success. Methods return bool (success/failure) or nullable types rather than throwing. Callers check return values to determine whether the native operation succeeded.

text
Pattern:
  if (handle == IntPtr.Zero) return false;
  if (!PInvoke.IsWindow(handle)) return false;
  try {
      NativeCall(handle, ...);
      return true;
  } catch {
      return false;
  }

Platform Considerations

  • 32-bit vs 64-bit: SetWindowLongPtr does not exist as a distinct entry point on 32-bit Windows. The custom PInvoke.cs handles this by routing to SetWindowLong on x86 and SetWindowLongPtrW on x64.
  • Windows 10 vs 11: Many DWM attributes (e.g., DWMWA_SYSTEMBACKDROP_TYPE) are only available on Windows 11 (build 22000+). The Utilities class gates these calls.
  • COM activation: ITaskbarList4 and IUISettings3 require COM activation. These are wrapped to handle COMException gracefully.

Architecture Decisions ADR 001 Multi Target Framework (`docs/architecture/decisions/ADR-001-multi-target-framework.md`)

ADR-001: Multi-Target Framework Support

Status

Accepted

Context

The WPF UI library needs to support a wide range of .NET implementations to maximize compatibility with existing projects while leveraging modern .NET features where available.

Supported Frameworks

  • .NET 10, 9, 8 - Modern .NET with Windows-specific APIs
  • .NET Framework 4.8.1, 4.7.2, 4.6.2 - Legacy enterprise applications
  • .NET Standard 2.0, 2.1 - Abstractions library only (maximizes compatibility)

Decision

Core Library (Wpf.Ui)

Target frameworks: net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462

Rationale:

  • Windows-specific project requires -windows TFM suffix for .NET 5+
  • .NET Framework support ensures compatibility with legacy WPF applications
  • Multi-version .NET support provides upgrade path for consumers

Abstractions Library (Wpf.Ui.Abstractions)

Target frameworks: net10.0;net9.0;net8.0;net462;netstandard2.1;netstandard2.0

Rationale:

  • No WPF dependencies allows broader compatibility
  • .NET Standard 2.0 enables use in class libraries shared between .NET Framework and .NET Core/5+
  • AOT-compatible (aot_compatible: true) for modern deployment scenarios

DI Integration (Wpf.Ui.DependencyInjection)

Target frameworks: Same as Abstractions

Rationale:

  • Depends only on Microsoft.Extensions.DependencyInjection.Abstractions (version 3.1.0 for broad compatibility)
  • No WPF dependencies
  • Enables DI integration in non-WPF contexts (e.g., background services)

Central Package Management

All package versions are managed in Directory.Packages.props:

xml
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup>
    
    <PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.242" />
    <PackageVersion Include="System.Memory" Version="4.6.3" />
  </ItemGroup>
</Project>

Individual projects reference packages without version attributes:

xml
<PackageReference Include="Microsoft.Windows.CsWin32" />

Benefits:

  • Single source of truth for package versions
  • Prevents version conflicts across projects
  • Simplifies dependency updates

Conditional Compilation

Framework Detection

csharp
#if NET5_0_OR_GREATER
    // Modern .NET APIs (Environment.OSVersion)
#else
    // .NET Framework fallback (registry)
#endif

#if NET6_0_OR_GREATER
    // DisposeAsync for CancellationTokenRegistration
#endif

#if NET8_0_OR_GREATER
    // Latest .NET 8+ features
#endif

Framework-Specific Dependencies

xml
<PackageReference Include="System.ValueTuple" Condition="'$(TargetFramework)' == 'net462'" />


<PackageReference Include="System.Drawing.Common" Condition="'$(TargetFramework)' != 'net462'" />

PolySharp Integration

Package: PolySharp (build-time source generator)

Provides polyfills for newer C# features on older target frameworks:

  • C# 11+ features on .NET 6/7/8
  • C# 12+ features on older .NET versions

Configuration:

xml
<ItemGroup>
  <CompilerVisibleProperty Include="PolySharpExcludeGeneratedTypes" />
</ItemGroup>

<PropertyGroup>
  
  <PolySharpExcludeGeneratedTypes>
    System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute;
    System.Diagnostics.CodeAnalysis.UnscopedRefAttribute
  </PolySharpExcludeGeneratedTypes>
</PropertyGroup>

Benefits:

  • Use modern C# syntax across all target frameworks
  • Init-only properties on .NET Framework
  • Required members support
  • CallerArgumentExpression on older frameworks

Language Version

Set to C# 14.0 across all projects:

xml
<LangVersion>14.0</LangVersion>

Note: Wpf.Ui.csproj overrides this to <LangVersion>preview</LangVersion> to enable C# preview features.

Combined with PolySharp, this enables:

  • Latest C# language features
  • Polyfills generated at compile-time for older frameworks
  • No runtime dependencies

Enforcement

MUST Follow

  1. All package versions in Directory.Packages.props only — Central Package Management is the single source of truth for NuGet versions
  2. Use -windows TFM suffix for projects with WPF dependencies (e.g., net10.0-windows, not net10.0)
  3. Use netstandard2.0/netstandard2.1 for abstraction-only packages that have no WPF or Windows dependency
  4. Guard framework-specific code with #if NET{X}_0_OR_GREATER directives — never use runtime version checks for compile-time API differences
  5. Use PolySharp for C# polyfills — never hand-write polyfill classes for language features (e.g., IsExternalInit, CallerArgumentExpression)
  6. Set LangVersion in Directory.Build.props — override in individual .csproj only when justified (currently only Wpf.Ui.csproj overrides to preview)

MUST NOT Do

  1. Never add Version attribute to PackageReference in .csproj files — all versions must be in Directory.Packages.props
  2. Never add a new TFM without updating all projects that share the same TFM set — all projects in a TFM group must stay in sync
  3. Never use #if with specific patch versions (e.g., NET8_0_10) — only use _OR_GREATER suffixed symbols
  4. Never remove a TFM from a shipping package without a major version bump — removing a TFM is a breaking change for consumers on that framework

Verification

  • CPM enforcement: <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> in Directory.Build.props causes build errors if Version is specified in .csproj
  • TFM validation: Build compiles all target frameworks on every dotnet build — missing APIs surface as compile errors immediately
  • PolySharp coverage: PolySharp source generator runs at build time and provides polyfills automatically; manual polyfills would cause duplicate symbol errors

Consequences

Positive

  • Broad Compatibility: Supports applications from .NET Framework 4.6.2 through .NET 10
  • Modern Development: C# 14 preview features via PolySharp
  • Simplified Maintenance: Central package management
  • Clear Upgrade Path: Consumers can upgrade .NET version without changing library
  • AOT Ready: Abstractions library supports AOT scenarios

Negative

  • Increased Build Complexity: Each commit builds 6+ framework variants
  • Larger Package Size: Multi-target packages include assemblies for all frameworks
  • Conditional Compilation: Requires #if directives for framework-specific code
  • Testing Burden: Features should be tested on multiple framework versions

Implementation Details

Directory.Build.props

Central build properties defined once:

xml
<PropertyGroup>
  <Version>4.2.0</Version>
  <LangVersion>14.0</LangVersion>
  <Nullable>enable</Nullable>
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

Conditional Property Groups

xml
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
  <DefineConstants>$(DefineConstants);NET8_0_OR_GREATER</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
  <DefineConstants>$(DefineConstants);BELOW_NET8</DefineConstants>
</PropertyGroup>

References


Architecture Decisions ADR 002 Control Library Architecture (`docs/architecture/decisions/ADR-002-control-library-architecture.md`)

ADR-002: Control Library Architecture

Status

Accepted

Context

The WPF UI library provides 77+ Fluent Design System controls. The architecture must support:

  • Clear code organization
  • XAML implicit styling
  • Type-safe intellisense
  • Easy discovery for developers
  • Maintainable codebase at scale

Decision

Folder-Per-Control Structure

Each control resides in its own subfolder under src/Wpf.Ui/Controls/{ControlName}/:

text
Controls/
├── Button/
│   ├── Button.cs              # Control class
│   └── Button.xaml            # Implicit style ResourceDictionary
├── NavigationView/
│   ├── NavigationView.Base.cs         # Core logic
│   ├── NavigationView.Properties.cs   # Dependency properties
│   ├── NavigationView.Events.cs       # Routed events
│   ├── NavigationView.Navigation.cs   # Navigation logic
│   ├── NavigationView.TemplateParts.cs # Template part bindings
│   ├── NavigationView.AttachedProperties.cs
│   └── NavigationView.xaml            # Implicit style
└── ContentDialog/
    ├── ContentDialog.cs
    ├── ContentDialog.FocusBehavior.cs  # Focused concern
    ├── ContentDialogHost.cs            # Host control
    ├── ContentDialogHostBehavior.cs
    ├── EventArgs/                      # Supporting types
    └── ContentDialog.xaml

Benefits:

  • Physical isolation prevents coupling between controls
  • Easy to locate all files related to a control
  • Supports partial class decomposition for complex controls
  • Clear ownership boundaries

Paired .cs + .xaml Files

Each control consists of:

  1. {ControlName}.cs - Control class with code-behind
  2. {ControlName}.xaml - ResourceDictionary with implicit style

Control Class Pattern:

csharp
// Controls/Button/Button.cs
namespace Wpf.Ui.Controls; // Flat namespace
// ReSharper disable once CheckNamespace

public class Button : System.Windows.Controls.Button, IAppearanceControl, IIconControl
{
    static Button()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(Button),
            new FrameworkPropertyMetadata(typeof(Button))
        );
    }

    // Dependency properties
    public static readonly DependencyProperty IconProperty =
        DependencyProperty.Register(nameof(Icon), ...);
}

XAML Style Pattern:

xaml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:Wpf.Ui.Controls">

    <Thickness x:Key="ButtonPadding">11,5,11,6</Thickness>

    <Style TargetType="{x:Type controls:Button}">
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:Button}">
                    
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Flat Namespace Strategy

All controls use a single namespace: Wpf.Ui.Controls

csharp
// Physical path: Controls/Button/Button.cs
namespace Wpf.Ui.Controls;  // NOT Wpf.Ui.Controls.Button
// ReSharper disable once CheckNamespace  // Suppress warning

Rationale:

  • Simpler XAML namespace mapping (xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml")
  • No need for consumers to know physical folder structure
  • Consistent with WPF framework controls (all in System.Windows.Controls)
  • Better intellisense experience (all controls in one namespace dropdown)

Trade-off: IDE warning suppression required (IDE0130: CheckNamespace)

Partial Class Decomposition

Complex controls split across multiple files:

NavigationView example:

  • NavigationView.Base.cs - Core logic, template application
  • NavigationView.Properties.cs - 27 dependency properties
  • NavigationView.Events.cs - 7 routed events
  • NavigationView.Navigation.cs - Navigation journal logic
  • NavigationView.TemplateParts.cs - Template part fields and bindings
  • NavigationView.AttachedProperties.cs - Attached properties

ContentDialog example:

  • ContentDialog.cs - Main implementation (714 lines)
  • ContentDialog.FocusBehavior.cs - Keyboard focus management

Benefits:

  • Files stay under 300-500 lines (typically)
  • Clear separation of concerns
  • Easy to navigate specific aspects
  • Reduces merge conflicts

Naming Convention: {ControlName}.{Concern}.cs

DependencyProperty Pattern

Registration:

csharp
/// <summary>Identifies the <see cref="Icon"/> dependency property.</summary>
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
    nameof(Icon),
    typeof(IconElement),
    typeof(Button),
    new PropertyMetadata(null, OnIconChanged, IconElement.Coerce)
);

CLR Wrapper:

csharp
[Bindable(true)]
[Category("Appearance")]
public IconElement? Icon
{
    get => (IconElement?)GetValue(IconProperty);
    set => SetValue(IconProperty, value);
}

Callback Pattern:

csharp
private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is Button button)
    {
        button.UpdateIconVisibility();
    }
}

Capability Interfaces

Controls implement interfaces for cross-cutting capabilities:

IAppearanceControl

csharp
public interface IAppearanceControl
{
    ControlAppearance Appearance { get; set; }
}

public enum ControlAppearance
{
    Primary, Secondary, Info, Dark, Light,
    Danger, Success, Caution, Transparent
}

Used by: Button, Badge, Snackbar, HyperlinkButton

IIconControl

csharp
public interface IIconControl
{
    IconElement? Icon { get; set; }
}

Used by: Button, NavigationViewItem, AutoSuggestBox

IThemeControl

csharp
public interface IThemeControl
{
    Appearance.ApplicationTheme ApplicationTheme { get; }
}

Used by: TitleBar, controls that need direct theme awareness

Benefits:

  • Type-safe capability detection
  • Shared behavior implementation
  • Consistent property naming across controls

Control Categories

Window Chrome

FluentWindow, TitleBar, ClientAreaBorder, Window

NavigationView, NavigationViewItem, BreadcrumbBar, TabControl, TabView, Menu

Buttons

Button, HyperlinkButton, DropDownButton, SplitButton, ToggleButton, ToggleSwitch

Text Input

TextBox, PasswordBox, RichTextBox, AutoSuggestBox, NumberBox

Dialogs & Overlays

ContentDialog, ContentDialogHost, MessageBox, Flyout, Snackbar, SnackbarPresenter

Data Display

Card, InfoBar, InfoBadge, Badge, ListView, DataGrid, TreeView

Pickers

CalendarDatePicker, DatePicker, TimePicker, ColorPicker

Progress & Feedback

ProgressBar, ProgressRing, RatingControl, ThumbRate

Icons

IconElement, FontIcon, SymbolIcon, ImageIcon, IconSourceElement

Layout

Anchor, Page, Frame, Expander, Separator, Slider

Enforcement

MUST Follow

  1. Each control in its own subfolder under Controls/{ControlName}/
  2. Paired .cs + .xaml files with matching names
  3. Flat namespace Wpf.Ui.Controls for all controls
  4. Static constructor with DefaultStyleKeyProperty.OverrideMetadata
  5. Implicit style in XAML with TargetType (no x:Key)
  6. OverridesDefaultStyle=True in all control styles
  7. SnapsToDevicePixels=True in all control styles
  8. XML documentation with `` and <example> for public API

MUST NOT Do

  1. Never nest controls in subdirectories (keep flat under Controls/)
  2. Never use different namespace from Wpf.Ui.Controls
  3. Never create keyed styles as primary style (use implicit TargetType)
  4. Never reference controls by folder structure in documentation

Verification

  • IDE0130 (CheckNamespace) suppressed in .editorconfig
  • WpfAnalyzers enforces DependencyProperty correctness
  • StyleCop rules enforce XML documentation (SA1600 suppressed for internal members)

Consequences

Positive

  • Highly Discoverable: Single namespace for all 77+ controls
  • Scalable: Adding new controls doesn't affect existing structure
  • Maintainable: Clear boundaries and partial class decomposition
  • Type-Safe: Interface-based capabilities enable polymorphism
  • Consistent: Uniform naming and organization patterns

Negative

  • IDE Warnings: Namespace/folder mismatch requires suppression
  • Large Directory: 77+ control folders in single directory
  • No Categorization: Physical structure doesn't reflect logical categories
  • Partial Class Complexity: Large controls split across many files

Alternatives Considered

Nested Category Folders

text
Controls/
├── Buttons/
│   ├── Button/
│   └── ToggleSwitch/
└── Navigation/
    └── NavigationView/

Rejected: Would require category-specific namespaces or deeper folder/namespace mismatch.

Category-Based Namespaces

csharp
namespace Wpf.Ui.Controls.Buttons;
namespace Wpf.Ui.Controls.Navigation;

Rejected: Requires consumers to know category classification. Inconsistent with WPF framework patterns.

Single File Per Control

Rejected: Controls like NavigationView have 1000+ lines. Unmanageable in single file.

References

  • WPF Framework Control Architecture: System.Windows.Controls namespace
  • WinUI 3 Control Architecture: Flat namespace strategy
  • WPF Control Authoring

Architecture Decisions ADR 003 Win32 Interop Via Cswin32 (`docs/architecture/decisions/ADR-003-win32-interop-via-cswin32.md`)

ADR-003: Win32 Interop via CsWin32

Status

Accepted

Context

WPF UI requires extensive Win32 API access for features unavailable in standard WPF:

  • Desktop Window Manager (DWM) effects (Mica, Acrylic backdrops)
  • Window corner rounding (Windows 11)
  • Dark mode title bars
  • System tray icon management
  • System theme detection
  • Taskbar progress indicators

Traditional P/Invoke requires:

  • Manual function signature declarations
  • COM interface definitions
  • Struct layout definitions
  • Constant value definitions
  • Maintaining cross-architecture compatibility (x86/x64/ARM64)

Decision

Use CsWin32 Source Generator

Package: Microsoft.Windows.CsWin32 (build-time only, PrivateAssets="all")

Declaration File: src/Wpf.Ui/NativeMethods.txt

CsWin32 generates P/Invoke bindings at compile-time from Win32 metadata.

NativeMethods.txt Format

text
# DWM Functions
DwmIsCompositionEnabled
DwmSetWindowAttribute
DwmExtendFrameIntoClientArea
S_OK
SetWindowThemeAttribute
DWM_SYSTEMBACKDROP_TYPE
DWM_WINDOW_CORNER_PREFERENCE
DWMWA_COLOR_NONE
WTA_OPTIONS

# Window Management
GetDpiForWindow
GetForegroundWindow
IsWindowVisible
SetWindowRgn
GetWindowRect
GetSystemMetrics
WINDOW_STYLE

# COM Interfaces
ITaskbarList4
TaskbarList

# Wildcard Patterns
WM_*
HT*

CsWin32 automatically generates:

  • Function P/Invoke declarations
  • Struct definitions with correct layout
  • Enum types
  • COM interface wrappers
  • Foundation types (HWND, HRESULT, BOOL, etc.)

Generated Code Location

Namespace: Windows.Win32 and Windows.Win32.Foundation
Physical Location: obj/ directory (not committed to source control)

Usage:

csharp
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;

// Generated type-safe P/Invoke
HRESULT result = PInvoke.DwmSetWindowAttribute(
    new HWND(windowHandle),
    DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
    &darkMode,
    sizeof(BOOL)
);

Three-Layer Architecture

Layer 1: CsWin32 Generated Code

Purpose: Auto-generated P/Invoke declarations

Characteristics:

  • Compile-time generated
  • Type-safe API surface
  • Cross-architecture compatible
  • Not committed to source control

Layer 2: Managed Wrappers

Purpose: Safe, validated native API access

Location: src/Wpf.Ui/Interop/

UnsafeNativeMethods.cs

csharp
internal static class UnsafeNativeMethods
{
    public static unsafe bool ApplyWindowCornerPreference(
        IntPtr handle,
        WindowCornerPreference cornerPreference)
    {
        // Validation layer
        if (handle == IntPtr.Zero)
            return false;

        if (!PInvoke.IsWindow(new HWND(handle)))
            return false;

        // Type conversion
        DWM_WINDOW_CORNER_PREFERENCE pvAttribute =
            UnsafeReflection.Cast(cornerPreference);

        // Native call with exception handling
        try
        {
            HRESULT hr = PInvoke.DwmSetWindowAttribute(
                new HWND(handle),
                DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
                &pvAttribute,
                (uint)sizeof(DWM_WINDOW_CORNER_PREFERENCE)
            );

            return hr == HRESULT.S_OK;
        }
        catch
        {
            // Graceful degradation for unsupported OS versions
            return false;
        }
    }
}

Responsibilities:

  • Handle validation (IntPtr.Zero, IsWindow checks)
  • Exception suppression for cross-version compatibility
  • HRESULT → bool conversion
  • Type-safe enum conversions

Custom PInvoke.cs

Purpose: Supplement CsWin32 for missing/incorrect signatures

csharp
namespace Windows.Win32;

internal static partial class PInvoke
{
    // CsWin32 doesn't generate correct SetWindowLongPtr for 32/64-bit
    [DllImport("USER32.dll",
        ExactSpelling = true,
        EntryPoint = "SetWindowLongPtrW",
        SetLastError = true)]
    internal static extern nint SetWindowLongPtr(
        HWND hWnd,
        WINDOW_LONG_PTR_INDEX nIndex,
        nint dwNewLong
    );
}

Layer 3: High-Level Utilities

Purpose: Business logic and feature implementation

Locations:

  • src/Wpf.Ui/Win32/Utilities.cs - OS version detection
  • src/Wpf.Ui/Appearance/ - Theme managers
  • src/Wpf.Ui/Controls/FluentWindow/ - Window chrome
  • src/Wpf.Ui/Tray/ - System tray management

Characteristics:

  • Consumes Layer 2 safe wrappers
  • OS version feature gating
  • Business logic and state management

Handle Validation Pattern

Critical Requirement: All native calls MUST validate handles.

csharp
public static bool NativeOperation(IntPtr handle)
{
    // Step 1: Null check
    if (handle == IntPtr.Zero)
    {
        return false;
    }

    // Step 2: Verify window exists
    if (!PInvoke.IsWindow(new HWND(handle)))
    {
        return false;
    }

    // Step 3: Perform operation
    HRESULT hr = PInvoke.SomeWin32Function(new HWND(handle), ...);
    return hr == HRESULT.S_OK;
}

Rationale:

  • Handles can become invalid between retrieval and use
  • Window may be destroyed on background thread
  • Invalid handles cause native crashes
  • IsWindow is inexpensive (single User32 call)

Exception Handling Strategy

Philosophy: Native APIs fail silently across Windows versions. Prefer graceful degradation over exceptions.

csharp
try
{
    HRESULT hr = PInvoke.DwmSetWindowAttribute(...);
    return hr == HRESULT.S_OK;
}
catch (COMException)
{
    // API not available on this Windows version
    return false;
}
catch
{
    // Unexpected failure, degrade gracefully
    return false;
}

Suppressed Exceptions:

  • COMException - COM API failures
  • Win32Exception - Native API errors
  • EntryPointNotFoundException - API not available on OS version
  • DllNotFoundException - DLL not present

Conditional Compilation

Framework-Specific Code

csharp
#if NET5_0_OR_GREATER
    // Modern API available
    var version = Environment.OSVersion;
#else
    // Fallback for .NET Framework
    var version = GetVersionFromRegistry();
#endif

OS Version Feature Gating

csharp
// Windows 11+ only features
if (Win32.Utilities.IsOSWindows11OrNewer)
{
    UnsafeNativeMethods.ApplyWindowCornerPreference(
        handle,
        WindowCornerPreference.Round
    );
}

// DWM composition required
if (Win32.Utilities.IsCompositionEnabled)
{
    UnsafeNativeMethods.ApplyWindowBackdrop(
        handle,
        WindowBackdropType.Acrylic
    );
}

Enforcement

MUST Follow

  1. Add new Win32 APIs to NativeMethods.txt (never manual P/Invoke unless CsWin32 fails)
  2. Validate handles before all native calls (IntPtr.Zero + IsWindow)
  3. Return bool from wrapper methods indicating success
  4. Suppress exceptions in interop layer for compatibility
  5. Use unsafe keyword explicitly for pointer operations
  6. Feature-gate by OS version for version-specific APIs
  7. Use HRESULT == S_OK pattern for success checking
  8. Keep generated code private (internal/private visibility)

MUST NOT Do

  1. Never call PInvoke directly from high-level code (use UnsafeNativeMethods wrappers)
  2. Never skip handle validation (even if "guaranteed" valid)
  3. Never throw exceptions from interop wrappers (return false instead)
  4. Never assume API availability across Windows versions
  5. Never use var for native types (HRESULT, HWND, etc. - explicit types required)
  6. Never commit obj/ directory (contains generated code)

Verification

  • WpfAnalyzers enforces correct patterns
  • Code review checks handle validation
  • Multi-version testing (Windows 7, 8.1, 10, 11)

Consequences

Positive

  • Type Safety: CsWin32 generates correct signatures from metadata
  • Maintenance: Win32 metadata updates automatically benefit project
  • Cross-Platform: ARM64, x64, x86 handled automatically
  • Correctness: Struct layouts, calling conventions verified by Microsoft
  • Discoverability: IntelliSense for all Windows APIs
  • Build-Time Only: Zero runtime dependencies

Negative

  • Build-Time Dependency: Requires CsWin32 NuGet package
  • Opaque Generation: Generated code in obj/, harder to debug
  • NativeMethods.txt Maintenance: Must manually add new APIs (currently 34 lines, including wildcard patterns like WM_* and HT*)
  • Supplements Needed: Some APIs require manual P/Invoke (SetWindowLongPtr)
  • Learning Curve: Developers must understand NativeMethods.txt format

Performance Considerations

CsWin32 Performance

  • Zero overhead: Generated code identical to hand-written P/Invoke
  • Inlined by JIT: Same JIT optimization as manual declarations
  • No reflection: Compile-time code generation

Handle Validation Cost

  • IsWindow: Single User32 API call (~1-2 μs)
  • Negligible: Compared to DWM/window operations (hundreds of μs)
  • Essential: Prevents native crashes worth the cost

Alternatives Considered

Manual P/Invoke

Rejected:

  • High maintenance burden (200+ Win32 functions across project)
  • Error-prone struct layout definitions
  • Cross-architecture compatibility issues
  • No automatic updates from Windows SDK

ComWrappers

Rejected:

  • Only solves COM interop, not general P/Invoke
  • More complex than CsWin32
  • Limited to .NET 5+

PInvoke.net Snippets

Rejected:

  • Community-maintained, not authoritative
  • Copy-paste errors common
  • No compile-time verification
  • Inconsistent signature styles

Migration Guide

Adding New Win32 API

  1. Add to NativeMethods.txt:

    text
    DwmGetColorizationColor
  2. Rebuild project (CsWin32 generates code)

  3. Create managed wrapper in UnsafeNativeMethods.cs:

    csharp
    public static bool GetColorizationColor(out Color color)
    {
        try
        {
            HRESULT hr = PInvoke.DwmGetColorizationColor(out uint colorValue, out BOOL opaque);
            color = Color.FromArgb(...);
            return hr == HRESULT.S_OK;
        }
        catch
        {
            color = default;
            return false;
        }
    }
  4. Consume from high-level code:

    csharp
    if (UnsafeNativeMethods.GetColorizationColor(out Color color))
    {
        // Use color
    }

References


Architecture Decisions ADR 004 Static Managers For Theming (`docs/architecture/decisions/ADR-004-static-managers-for-theming.md`)

ADR-004: Static Managers for Theming

Status

Accepted

Context

The theming system requires global coordination across:

  • Application resource dictionary management
  • System theme synchronization
  • Accent color application
  • Window appearance updates

Multiple approaches exist:

  1. Static classes (global singleton)
  2. Instance-based services (registered in DI container)
  3. Ambient context pattern (ThreadStatic/AsyncLocal)

Decision

Use static class singleton pattern for core theme managers:

  • ApplicationThemeManager
  • ApplicationAccentColorManager
  • SystemThemeWatcher
  • WindowBackgroundManager
  • ResourceDictionaryManager (internal)

Implementation Pattern

csharp
public static class ApplicationThemeManager
{
    // Global state
    private static ApplicationTheme _currentTheme = ApplicationTheme.Unknown;

    // Global event
    public static event ThemeChangedEvent? Changed;

    // Static methods
    public static void Apply(ApplicationTheme theme)
    {
        if (_currentTheme == theme)
            return;

        ResourceDictionaryManager manager = new(LibraryNamespace);
        manager.UpdateDictionary("theme", GetThemeUri(theme));

        _currentTheme = theme;
        Changed?.Invoke(theme, GetSystemAccent());
    }

    public static ApplicationTheme GetAppTheme()
    {
        return _currentTheme;
    }
}

No Constructor, No Instances

csharp
public static class ApplicationThemeManager
{
    // All members are static
    // Cannot be instantiated
    // Cannot be inherited
    // Cannot be mocked/substituted
}

Rationale

Simple API Surface

csharp
// Immediate clarity of global scope
ApplicationThemeManager.Apply(ApplicationTheme.Dark);
var current = ApplicationThemeManager.GetAppTheme();

Compared to instance-based:

csharp
// Requires context about where themeManager comes from
_themeManager.Apply(ApplicationTheme.Dark);
var current = _themeManager.GetAppTheme();

Single Source of Truth

Application theme is fundamentally global state:

  • Only one theme can be active
  • All windows share the same theme
  • Resource dictionaries are process-wide

Static class enforces singleton semantics at compile-time.

No DI Configuration Required

csharp
// Works immediately without setup
public MainWindow()
{
    InitializeComponent();
    ApplicationThemeManager.Apply(ApplicationTheme.Dark);
}

Instance-based would require:

csharp
// App.xaml.cs
services.AddSingleton<IThemeManager, ThemeManager>();

// MainWindow.xaml.cs
public MainWindow(IThemeManager themeManager)
{
    _themeManager = themeManager;
    InitializeComponent();
    _themeManager.Apply(ApplicationTheme.Dark);
}

WPF Application Model Alignment

WPF itself uses static patterns extensively:

  • Application.Current (static property)
  • Application.Current.Resources (global resource dictionary)
  • SystemColors (static class)
  • SystemParameters (static class)

Trade-offs

Advantages

1. Simplicity

  • No DI configuration required
  • No interface abstraction needed
  • Clear that state is global
  • Immediate usability

2. Performance

  • Zero overhead (no interface dispatch)
  • No allocation for manager instances
  • Direct static method calls

3. Discoverability

  • Easy to find with IntelliSense
  • Self-documenting via naming (ApplicationThemeManager clearly global)
  • No need to understand DI container

4. Compatibility

  • Works in non-DI scenarios (simple WPF apps)
  • Compatible with .NET Framework patterns
  • No breaking changes if DI added later

Disadvantages

1. Limited Testability

  • Cannot mock static classes
  • Difficult to isolate in unit tests
  • Tests may affect each other through shared state

Mitigation:

  • Extract IThemeService for consumers who need testability
  • Test through integration tests instead of unit tests
  • Use [Collection] attribute in XUnit to isolate test state

2. Hidden Dependencies

  • Static call hides dependency
  • Harder to track theme manager usage
  • Violates dependency injection principle

Mitigation:

  • Theme management is intentionally global
  • Not a "hidden" dependency if explicitly documented as global

3. Global State

  • Mutable global state
  • Concurrent access concerns
  • No lifetime management

Mitigation:

  • Theme changes are inherently single-threaded (UI thread)
  • Application.Current.Resources is already global mutable state
  • No need for lifetime management (lives entire process lifetime)

4. No Polymorphism

  • Cannot substitute alternative implementations
  • Cannot extend behavior through inheritance

Mitigation:

  • Theme system is not extensible by design
  • Alternative implementations not a use case

Service Interface for DI

For consumers requiring testability, IThemeService wraps static managers:

csharp
public interface IThemeService
{
    ApplicationTheme GetTheme();
    SystemTheme GetNativeSystemTheme();
    ApplicationTheme GetSystemTheme();
    bool SetTheme(ApplicationTheme applicationTheme);
    bool SetSystemAccent();
    bool SetAccent(Color accentColor);
    bool SetAccent(SolidColorBrush accentSolidBrush);
}

public partial class ThemeService : IThemeService
{
    public ApplicationTheme GetTheme()
        => ApplicationThemeManager.GetAppTheme();

    public SystemTheme GetNativeSystemTheme()
        => ApplicationThemeManager.GetSystemTheme();

    public ApplicationTheme GetSystemTheme()
        => ApplicationThemeManager.GetSystemTheme() switch { ... };

    public bool SetTheme(ApplicationTheme applicationTheme)
    {
        ApplicationThemeManager.Apply(applicationTheme);
        return true;
    }

    public bool SetSystemAccent()
    {
        ApplicationAccentColorManager.ApplySystemAccent();
        return true;
    }

    public bool SetAccent(Color accentColor)
    {
        ApplicationAccentColorManager.Apply(accentColor);
        return true;
    }

    public bool SetAccent(SolidColorBrush accentSolidBrush)
    {
        ApplicationAccentColorManager.Apply(accentSolidBrush.Color);
        return true;
    }
}

Registration:

csharp
services.AddSingleton<IThemeService, ThemeService>();

Usage in testable code:

csharp
public class SettingsViewModel
{
    private readonly IThemeService _themeService;

    public SettingsViewModel(IThemeService themeService)
    {
        _themeService = themeService;
    }

    public void ApplyDarkMode()
    {
        _themeService.SetTheme(ApplicationTheme.Dark);
    }
}

Testing with mock:

csharp
[Fact]
public void ApplyDarkMode_CallsThemeService()
{
    // Arrange
    var mockThemeService = Substitute.For<IThemeService>();
    var viewModel = new SettingsViewModel(mockThemeService);

    // Act
    viewModel.ApplyDarkMode();

    // Assert
    mockThemeService.Received(1).SetTheme(ApplicationTheme.Dark);
}

UiApplication Pattern

UiApplication uses [ThreadStatic] for thread-local singleton:

csharp
public class UiApplication
{
    [ThreadStatic]
    private static UiApplication? _uiApplication;

    public static UiApplication? Current => _uiApplication;

    public UiApplication(Application application)
    {
        // Stores the application reference and sets _uiApplication
    }

    // Instance methods operate on the wrapped Application
    public ResourceDictionary Resources => Application.Current.Resources;
}

Rationale:

  • Non-static class instantiated with an Application parameter
  • Uses [ThreadStatic] backing field _uiApplication for thread-local singleton
  • Each UI thread gets its own UiApplication instance
  • Safely wraps Application.Current (which is also thread-local)
  • Allows future expansion with per-thread state

Enforcement

MUST Follow

  1. Use static managers directly for simple scenarios:

    csharp
    ApplicationThemeManager.Apply(ApplicationTheme.Dark);
  2. Use IThemeService in testable/DI-dependent code:

    csharp
    public ViewModel(IThemeService themeService) { }
  3. Document global nature in XML docs:

    csharp
    /// <summary>
    /// Global theme manager. Applies themes application-wide.
    /// </summary>
  4. Never create wrapper instances of static managers:

    csharp
    // BAD: Don't do this
    public class ThemeManagerWrapper
    {
        private ApplicationTheme _cachedTheme;
    
        public void Apply(ApplicationTheme theme)
        {
            _cachedTheme = theme;
            ApplicationThemeManager.Apply(theme);
        }
    }

MUST NOT Do

  1. Never attempt to mock static classes in unit tests

    • Use IThemeService instead if testing is needed
  2. Never cache theme state in instance fields

    • Always query ApplicationThemeManager.GetAppTheme() for current state
  3. Never create parallel theme systems

    • Static managers are the single source of truth

Verification

  • Code review enforces proper usage
  • Documentation clearly marks classes as static global managers
  • IThemeService provides tested alternative where needed

Testing Strategy

Integration Tests

Theme system is tested through integration tests that exercise full stack:

csharp
[Fact]
public async Task ThemeChange_UpdatesWindowAppearance()
{
    // Arrange
    var window = new FluentWindow();
    window.Show();

    // Act
    ApplicationThemeManager.Apply(ApplicationTheme.Dark);
    await Task.Delay(500); // Allow visual update

    // Assert
    var theme = ApplicationThemeManager.GetAppTheme();
    Assert.Equal(ApplicationTheme.Dark, theme);

    // Cleanup
    window.Close();
}

Unit Tests for Consumer Code

Consumer code uses IThemeService for testability:

csharp
[Fact]
public void ApplyDarkTheme_UpdatesCurrentTheme()
{
    var themeService = Substitute.For<IThemeService>();
    var viewModel = new SettingsViewModel(themeService);

    viewModel.ApplyDarkTheme();

    themeService.Received().SetTheme(ApplicationTheme.Dark);
}

Documentation Requirements

All static manager classes include XML doc warning:

csharp
/// <summary>
/// Global static manager for application theming.
/// </summary>
/// <remarks>
/// <para>
/// This is a static class managing process-wide theme state.
/// Theme changes affect all windows in the application.
/// </para>
/// <para>
/// For testable code, use <see cref="IThemeService"/> instead.
/// </para>
/// </remarks>
public static class ApplicationThemeManager
{
    // ...
}

Future Considerations

Potential Migration to Instances

If future requirements demand it, can migrate while maintaining compatibility:

csharp
// New instance-based implementation
public sealed class ThemeManager : IThemeManager
{
    // Instance implementation
}

// Static facade maintains compatibility
public static class ApplicationThemeManager
{
    private static readonly IThemeManager _instance = new ThemeManager();

    public static void Apply(ApplicationTheme theme)
        => _instance.Apply(theme);
}

This preserves existing code while enabling DI-based usage.

References


Architecture Decisions ADR 005 Feature Folder Controls (`docs/architecture/decisions/ADR-005-feature-folder-controls.md`)

ADR-005: Feature Folder Organization for Controls

Status

Accepted

Context

With 77+ controls in the library, code organization becomes critical for maintainability. The structure must support:

  • Clear isolation between controls
  • Easy location of control-related files
  • Logical grouping of complex control components
  • Scalability as new controls are added

Decision

Feature Folder per Control

Each control resides in Controls/{ControlName}/ directory:

text
Controls/
├── Button/
│   ├── Button.cs
│   └── Button.xaml
├── Card/
│   ├── Card.cs
│   └── Card.xaml
├── NavigationView/
│   ├── NavigationView.Base.cs
│   ├── NavigationView.Properties.cs
│   ├── NavigationView.Events.cs
│   ├── NavigationView.Navigation.cs
│   ├── NavigationView.TemplateParts.cs
│   ├── NavigationView.AttachedProperties.cs
│   ├── NavigationView.xaml
│   ├── NavigationViewItem.cs
│   ├── NavigationViewItemHeader.cs
│   └── NavigationViewItemSeparator.cs

One control = one folder with all related files.

Structure Patterns

Simple Controls

Pattern: Single .cs + .xaml pair

text
Badge/
├── Badge.cs        # Control implementation
└── Badge.xaml      # Implicit style

Applies to: Button, Badge, Card, InfoBar, TextBox, etc. (50+ controls)

Complex Controls with Partial Classes

Pattern: Multiple partial class files organized by concern

text
NavigationView/
├── NavigationView.Base.cs              # Core control logic
├── NavigationView.Properties.cs         # 27 dependency properties
├── NavigationView.Events.cs            # 7 routed events
├── NavigationView.Navigation.cs         # Page navigation logic
├── NavigationView.TemplateParts.cs     # Template part fields/binding
├── NavigationView.AttachedProperties.cs # Attached property definitions
└── NavigationView.xaml                 # Implicit style + template

Partial class naming: {ControlName}.{Concern}.cs

Applies to: NavigationView, ContentDialog, TitleBar

Pattern: Additional related controls in same folder

text
NavigationView/
├── NavigationView*.cs                   # Main control (6 files)
├── NavigationView.xaml
├── NavigationViewItem.cs               # Item container
├── NavigationViewItemHeader.cs         # Header item
├── NavigationViewItemSeparator.cs      # Separator
├── NavigationViewContentPresenter.cs   # Content host
├── INavigationView.cs                  # Interface
└── INavigationViewItem.cs              # Item interface

Rationale: Tightly coupled types that are only used together.

Controls with Supporting Subdirectories

Pattern: Subfolder for supporting types

text
ContentDialog/
├── ContentDialog.cs
├── ContentDialog.FocusBehavior.cs
├── ContentDialog.xaml
├── ContentDialogHost.cs
├── ContentDialogHostBehavior.cs
└── EventArgs/
    ├── ContentDialogButtonClickEventArgs.cs
    ├── ContentDialogClosingEventArgs.cs
    └── ContentDialogClosedEventArgs.cs

When to use subfolder:

  • 3+ related types (EventArgs, Converters, Enums)
  • Clear sub-component (e.g., EventArgs)

Flat Namespace Strategy

All controls use: Wpf.Ui.Controls namespace (regardless of folder depth)

csharp
// File: Controls/NavigationView/NavigationView.cs
namespace Wpf.Ui.Controls;  // Flat namespace, not Wpf.Ui.Controls.NavigationView
// ReSharper disable once CheckNamespace

Trade-off: Folder structure does not match namespace.

IDE Configuration Required:

ini
# .editorconfig
dotnet_diagnostic.IDE0130.severity = none  # Suppress namespace/folder mismatch

Partial Class Decomposition Strategies

By Concern

NavigationView splits by logical concern:

  • Base.cs - Control infrastructure (template application, initialization)
  • Properties.cs - Dependency property definitions
  • Events.cs - Routed event definitions
  • Navigation.cs - Page navigation logic
  • TemplateParts.cs - Template part fields and OnApplyTemplate logic
  • AttachedProperties.cs - Attached property definitions

By Feature

ContentDialog splits by feature:

  • ContentDialog.cs - Main implementation (async ShowAsync, dialog lifecycle)
  • ContentDialog.FocusBehavior.cs - Keyboard focus management (isolated behavior)

Guidelines for Splitting

When to split:

  • File exceeds 500 lines
  • Clear separation of concerns exists (properties vs. logic)
  • Feature is self-contained and isolatable

How to name:

  • Base.cs - Core control logic (template, initialization)
  • Properties.cs - All dependency properties
  • Events.cs - All routed events
  • {Feature}.cs - Specific feature (FocusBehavior, Animation, etc.)

Don't split:

  • Controls under 300 lines
  • No clear separation of concerns
  • When references would create circular dependencies

File Naming Conventions

Control Classes

text
{ControlName}.cs              # Simple control
{ControlName}.{Concern}.cs    # Partial class with concern

XAML Styles

text
{ControlName}.xaml            # Implicit style ResourceDictionary

Supporting Types

text
{TypePurpose}{ControlName}.cs # e.g., NavigationViewItem
{ControlName}{Purpose}.cs     # e.g., ContentDialogHost
I{ControlName}.cs             # Interface

Event Arguments

text
{ControlName}{EventName}EventArgs.cs

Examples:

  • ContentDialogButtonClickEventArgs.cs
  • NavigatedEventArgs.cs

Enforcement

MUST Follow

  1. One control = one folder under Controls/

  2. Paired .cs + .xaml with matching names

  3. Flat namespace Wpf.Ui.Controls for all controls

  4. Partial class naming {ControlName}.{Concern}.cs

  5. ReSharper suppress comment when namespace doesn't match folder:

    csharp
    namespace Wpf.Ui.Controls;
    // ReSharper disable once CheckNamespace
  6. Keep related types together in same folder when tightly coupled

MUST NOT Do

  1. Never nest control folders (keep flat under Controls/)

    text
    ❌ Controls/Buttons/Button/
    ✅ Controls/Button/
  2. Never use category-specific namespace

    csharp
    ❌ namespace Wpf.Ui.Controls.Buttons;
    ✅ namespace Wpf.Ui.Controls;
  3. Never split files arbitrarily (must have clear separation of concerns)

  4. Never create more than 2 directory levels under Controls/

    text
    ✅ Controls/ContentDialog/EventArgs/
    ❌ Controls/ContentDialog/EventArgs/Closing/

Verification

  1. IDE0130 suppressed in .editorconfig for namespace/folder mismatch
  2. Code review checks folder organization
  3. Naming conventions enforced during PR review

Benefits

Developer Experience

Easy to Find:

bash
# Looking for Button control?
Controls/Button/Button.cs         # Immediately obvious location

Clear Boundaries:

  • All Button-related code in Controls/Button/
  • No cross-control dependencies (enforced by folder isolation)

Scalable:

  • Adding new control = create new folder
  • Doesn't affect existing controls

Maintainability

Isolation:

  • Changes to Button don't affect Card
  • Merge conflicts localized to single control

Discoverability:

  • New developers find controls by folder name
  • Related types co-located (NavigationViewItem with NavigationView)

Refactoring:

  • Easy to split large files (add .Properties.cs)
  • Clear when to split (file size, concern separation)

Build Performance

Partial Classes:

  • Compiler can parallelize partial class compilation
  • Changes to Properties.cs don't trigger recompilation of Navigation.cs

Trade-offs

Advantages

  • ✅ Clear physical organization
  • ✅ Easy to locate control files
  • ✅ Enforces encapsulation (hard to reference across control folders)
  • ✅ Scales to 100+ controls
  • ✅ Supports complex controls with many files

Disadvantages

  • ❌ Folder/namespace mismatch requires suppression
  • ❌ 77+ folders in single directory (large directory)
  • ❌ No physical categorization (all controls appear equal)
  • ❌ Related controls may be far apart alphabetically (Button vs. ToggleButton)

Alternatives Considered

Category-Based Folders

text
Controls/
├── Buttons/
│   ├── Button/
│   └── ToggleButton/
└── Navigation/
    └── NavigationView/

Rejected:

  • Requires category-specific namespaces or deeper mismatch
  • Category classification is subjective (is ToggleSwitch a Button or Input?)
  • Doesn't align with flat namespace strategy

Single File Per Control

text
Controls/
├── Button.cs
├── Button.xaml
├── NavigationView.cs
└── NavigationView.xaml

Rejected:

  • Complex controls (NavigationView 1000+ lines) unmanageable
  • Supporting types have unclear location
  • 150+ files in single directory

Hybrid Categorization

text
Controls/
├── Button/
├── Input/
│   ├── TextBox/
│   └── NumberBox/
└── Navigation/
    └── NavigationView/

Rejected:

  • Inconsistent structure (some categories, some not)
  • Unclear where new controls go
  • Complicates namespace strategy

Migration Path

Adding New Simple Control

  1. Create folder Controls/{NewControl}/
  2. Add {NewControl}.cs with control class
  3. Add {NewControl}.xaml with implicit style
  4. Add ReSharper suppress comment to .cs file

Splitting Existing Control

Example: Card becomes too large

Before:

text
Card/
├── Card.cs (500 lines)
└── Card.xaml

After:

text
Card/
├── Card.Base.cs (200 lines - core logic)
├── Card.Properties.cs (100 lines - dependency properties)
├── Card.Animation.cs (100 lines - animation logic)
└── Card.xaml

Refactoring steps:

  1. Extract dependency properties to Card.Properties.cs
  2. Extract animation logic to Card.Animation.cs
  3. Keep core logic in Card.Base.cs
  4. All files use partial class Card

Documentation

Control Folder README

Each complex control folder includes README.md:

markdown
# NavigationView

Complex navigation container with 6 partial class files:

- **Base.cs** - Core control logic, template application
- **Properties.cs** - 27 dependency properties
- **Events.cs** - 7 routed events
- **Navigation.cs** - Page navigation, journal, back/forward
- **TemplateParts.cs** - Template part bindings
- **AttachedProperties.cs** - HeaderContent attached property

Related types:
- NavigationViewItem - Selectable item container
- NavigationViewItemHeader - Non-selectable header
- INavigationView - Public control interface

References


Architecture Views Context (`docs/architecture/views/context.md`)

C4 Context Diagram (Level 1)

This document describes the system context of WPF UI -- the external actors, systems, and data flows that surround the library.

Context Diagram

text
/* Detailed source-code truncated for AI context efficiency. */

External Actors

Library Consumers (WPF Developers)

The primary users of WPF UI are C#/WPF developers who want to apply Microsoft Fluent Design System styling to their desktop applications. They interact with the library by:

  1. Installing NuGet packages -- WPF-UI (core), optionally WPF-UI.DependencyInjection, WPF-UI.Tray, WPF-UI.Abstractions
  2. Adding XAML resource dictionaries -- and in App.xaml
  3. Using controls in XAML -- Via the xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" namespace prefix
  4. Calling services in C# -- INavigationService, IContentDialogService, ISnackbarService, ApplicationThemeManager

End Users

End users interact with consumer WPF applications built using WPF UI. They experience Fluent Design visuals (rounded corners, Mica backdrop, accent colors) and interact with the 77+ controls. End users do not directly interact with the WPF UI library.

Windows OS

WPF UI makes extensive use of the Windows platform through several channels:

API Surface Usage Key Functions
DWM (Desktop Window Manager) Backdrop effects (Mica, Acrylic, Tabbed), dark mode, window corner preferences DwmSetWindowAttribute, DwmIsCompositionEnabled
User32 Window style manipulation, message pump interception, snap layouts SetWindowLong, GetWindowLong, SetWindowLongPtr
Shell32 System tray icons, taskbar progress Shell_NotifyIcon, ITaskbarList4 COM
WinRT UISettings System accent colors (8-color palette) IUISettings3 COM interface
Registry Fallback for accent colors, OS version detection DWM\AccentColor, CurrentVersion
WndProc Messages Theme change detection, title bar hit testing WM_THEMECHANGED, WM_DWMCOLORIZATIONCOLORCHANGED, WM_NCHITTEST

NuGet.org

The primary distribution channel for WPF UI. The GitHub Actions CD pipeline (wpf-ui-cd-nuget.yaml) publishes the following packages on every version bump (triggered by Directory.Build.props changes on the main branch):

Package ID Project Description
WPF-UI Wpf.Ui Core library with controls, theming, services
WPF-UI.Abstractions Wpf.Ui.Abstractions Interface contracts for navigation
WPF-UI.DependencyInjection Wpf.Ui.DependencyInjection MS DI integration
WPF-UI.Tray Wpf.Ui.Tray System tray icon support

Visual Studio Marketplace

Distributes the Wpf.Ui.Extension VSIX for Visual Studio 2022 (x64 and arm64). Built by the wpf-ui-cd-extension.yaml workflow. Provides project templates for creating new WPF UI applications.

GitHub

Hosts the source repository, runs CI/CD via GitHub Actions (7 workflows), and serves the DocFX documentation site via GitHub Pages.

Data Flows

NuGet Package Distribution

mermaid
flowchart LR
    Dev["Developer Machine"]
    NuGet["NuGet.org"]
    GH["GitHub Actions CD"]

    GH -->|"nuget push *.nupkg<br/>Strong-name signed<br/>SourceLink embedded"| NuGet
    NuGet -->|"dotnet add package WPF-UI<br/>Downloads .nupkg + .snupkg"| Dev

    style Dev fill:#e1f5ff,stroke:#0277bd
    style NuGet fill:#fff4e1,stroke:#f57f17
    style GH fill:#e8f5e9,stroke:#2e7d32

Win32 API Calls (Runtime)

text
Consumer WPF App
     |
     |  Uses FluentWindow / TitleBar / SystemThemeWatcher
     |
     v
  Wpf.Ui Core Library
     |
     |-- UnsafeNativeMethods (handle validation layer)
     |      |
     |      |-- CsWin32 PInvoke (source-generated declarations)
     |             |
     |             v
     |      Windows.Win32 namespace
     |
     v
  Windows OS Kernel / DWM / Shell32 / User32

XAML Resource Loading

text
Consumer App.xaml
     |
     |  <ui:ThemesDictionary Theme="Dark"/>
     |  <ui:ControlsDictionary/>
     |
     v
  ThemesDictionary (Markup Extension)
     |  Resolves pack URI:
     |  pack://application:,,,/Wpf.Ui;component/Resources/Theme/Dark.xaml
     |
     v
  ControlsDictionary (Markup Extension)
     |  Resolves pack URI:
     |  pack://application:,,,/Wpf.Ui;component/Resources/Wpf.Ui.xaml
     |     --> Merges all 77 control .xaml ResourceDictionaries
     |     --> Loads font resources (FluentSystemIcons)
     |     --> Loads palette, typography, variables
     |
     v
  WPF Resource System
     |  Controls resolve styles via DefaultStyleKeyProperty
     |  Theme brushes resolved via DynamicResource
     |  Accent colors updated programmatically by ApplicationAccentColorManager

Theme Change Flow

text
Windows OS
     |
     |  WM_DWMCOLORIZATIONCOLORCHANGED
     |  WM_THEMECHANGED
     |  WM_SYSCOLORCHANGE
     |
     v
  SystemThemeWatcher (HwndSource.AddHook)
     |
     |  Detects OS theme change
     |
     v
  ApplicationThemeManager.ApplySystemTheme()
     |
     |-- ResourceDictionaryManager.UpdateDictionary("theme", newThemeUri)
     |      Swaps Light.xaml <-> Dark.xaml in MergedDictionaries
     |
     |-- ApplicationAccentColorManager.Apply(accentColor, theme)
     |      Updates 20+ dynamic color resources
     |
     |-- WindowBackgroundManager.UpdateBackground(window)
     |      Applies DWM dark mode attribute + backdrop effect
     |
     |-- Fires ApplicationThemeManager.Changed event
     |      --> Subscribers (CodeBlock, InternalNotifyIconManager, consumer code)

Architecture Views Logical Architecture (`docs/architecture/views/logical-architecture.md`)

Logical Architecture

This document describes the logical architecture of WPF UI v4.2.0, a C# WPF control library implementing Microsoft Fluent Design System. It covers module dependencies, internal layer organization, control authoring patterns, cross-cutting concerns, and the multi-targeting strategy.


1. Module/Package Dependency Diagram

The WPF UI solution consists of ten projects organized around a core library (Wpf.Ui) with satellite packages for optional functionality.

text
/* Detailed source-code truncated for AI context efficiency. */

Dependency rules:

Package Direct dependencies
Wpf.Ui.Abstractions None (zero external dependencies)
Wpf.Ui Wpf.Ui.Abstractions, Microsoft.Windows.CsWin32 (build-time), System.Memory
Wpf.Ui.DependencyInjection Wpf.Ui.Abstractions, Microsoft.Extensions.DependencyInjection.Abstractions 3.1.0
Wpf.Ui.Tray Wpf.Ui, System.Drawing.Common
Wpf.Ui.SyntaxHighlight Wpf.Ui
Wpf.Ui.ToastNotifications None (standalone stub)
Wpf.Ui.Gallery Wpf.Ui, Wpf.Ui.DependencyInjection, Wpf.Ui.Tray, Wpf.Ui.SyntaxHighlight, Wpf.Ui.ToastNotifications, CommunityToolkit.Mvvm, Microsoft.Extensions.Hosting
Wpf.Ui.FlaUI FlaUI.Core
Wpf.Ui.FontMapper None
Wpf.Ui.Extension VSIX SDK (VS 2022), template projects (Blank, Compact, Fluent)

2. Core Library Internal Structure (Layer Diagram)

The src/Wpf.Ui/ project is organized into six logical layers, from user-facing controls down to Win32 primitives.

text
/* Detailed source-code truncated for AI context efficiency. */

Layer Details

Layer 1 -- Controls (77 control folders)

Each control follows a folder-per-control pattern: Controls/{Name}/{Name}.cs + Controls/{Name}/{Name}.xaml.

Controls extend WPF base classes (ContentControl, Control, Button, ToggleButton, Window, etc.) and optionally implement WPF UI interfaces: IAppearanceControl (accent/appearance support), IIconControl (icon support), IThemeControl (theme awareness), IDpiAwareControl (DPI awareness).

Complex controls are split into partial classes by concern. For example, NavigationView spans six partial files:

File Responsibility
NavigationView.Base.cs Core logic, constructor, static constructor
NavigationView.Properties.cs DependencyProperty registrations
NavigationView.Events.cs RoutedEvent registrations
NavigationView.Navigation.cs Page navigation logic
NavigationView.TemplateParts.cs Template part bindings (OnApplyTemplate)
NavigationView.AttachedProperties.cs Attached property definitions

Key controls: FluentWindow, NavigationView, TitleBar, ContentDialog, NumberBox, AutoSuggestBox, ToggleSwitch, Snackbar, BreadcrumbBar, InfoBar, RatingControl, ProgressRing.

Layer 2 -- Appearance/Theming

Class Pattern Responsibility
ApplicationThemeManager Static Runtime theme switching via ResourceDictionary swap. Fires ThemeChangedEvent.
ApplicationAccentColorManager Static Updates 20+ dynamic color resources (SystemAccentColor, AccentFillColorDefault, etc.) from WinRT UISettings or registry fallback.
SystemThemeWatcher Static Hooks WndProc for WM_THEMECHANGED, WM_DWMCOLORIZATIONCOLORCHANGED, WM_SYSCOLORCHANGE. Auto-syncs app theme with OS.
WindowBackgroundManager Static Applies DWM backdrop effects (Mica, Acrylic, Tabbed) via DwmSetWindowAttribute.
ResourceDictionaryManager Internal URI-based dictionary search and swap within Application.Resources.MergedDictionaries.

Six theme files: Light.xaml, Dark.xaml, HC1.xaml, HC2.xaml, HCBlack.xaml, HCWhite.xaml.

Layer 3 -- Services

Service implementations are defined at the src/Wpf.Ui/ root level alongside their interface files.

Service Interface Wraps
NavigationService INavigationService INavigationView control
ContentDialogService IContentDialogService ContentDialog control
SnackbarService ISnackbarService Snackbar control
ThemeService IThemeService ApplicationThemeManager static class
TaskBarService ITaskBarService COM ITaskbarList4 via Win32 interop

Layer 4 -- Infrastructure

Component Contents
Converters/ 18 IValueConverter implementations: BoolToVisibilityConverter, BrushToColorConverter, EnumToBoolConverter, IconSourceElementConverter, CornerRadiusSplitConverter, ProgressThicknessConverter, etc.
Extensions/ 14 extension method classes: ColorExtensions, FrameExtensions, NavigationServiceExtensions, SnackbarServiceExtensions, SymbolExtensions, UiElementExtensions, etc.
Markup/ XAML markup extensions: ControlsDictionary, ThemesDictionary, SymbolIconExtension, FontIconExtension, ImageIconExtension, ThemeResourceExtension, Design.
Animations/ TransitionAnimationProvider (applies FadeIn, SlideBottom, SlideRight, etc.), Transition enum, AnimationProperties. Checks HardwareAcceleration.RenderingTier before animating.
Input/ IRelayCommand, IRelayCommand<T>, RelayCommand<T> -- lightweight command implementations.
Hardware/ DpiHelper, DisplayDpi, HardwareAcceleration, RenderingTier -- DPI detection and rendering tier evaluation.
AutomationPeers/ CardControlAutomationPeer, ContentDialogAutomationPeer. Controls with automation peers override OnCreateAutomationPeer().
Taskbar/ TaskbarProgress, TaskbarProgressState -- Windows taskbar progress bar manipulation.

Layer 5 -- Win32 Interop

A three-layer architecture for native Windows API access:

  1. CsWin32 auto-generation (NativeMethods.txt lists 35 Win32 functions/types). The Microsoft.Windows.CsWin32 source generator produces P/Invoke signatures in the Windows.Win32 namespace. Covers DWM (DwmSetWindowAttribute, DwmIsCompositionEnabled), User32 (SetWindowLong, GetDpiForWindow, GetForegroundWindow), Shell32 (ITaskbarList4), and associated structs/enums.

  2. Managed wrappers (Interop/):

    • UnsafeNativeMethods.cs -- safe wrappers with handle validation (IntPtr.Zero check + PInvoke.IsWindow) before calling CsWin32-generated methods. Methods like ApplyWindowCornerPreference, ApplyBorderColor, RemoveWindowTitlebarContents.
    • PInvoke.cs -- manual [DllImport] for SetWindowLongPtr (not generated by CsWin32 for all overloads).
    • UnsafeReflection.cs -- type casting helpers for enum-to-Win32-struct conversion.
  3. OS utilities (Win32/):

    • Utilities.cs -- OS version detection (Vista, Windows 7, 8, 10, 11 build checks), DWM composition availability, system theme detection via IUISettings3 COM interface.

Layer 6 -- Resources

Path Contents
Resources/Theme/ 6 XAML theme dictionaries (Light, Dark, HC1, HC2, HCBlack, HCWhite)
Resources/Fonts/ FluentSystemIcons-Filled.ttf, FluentSystemIcons-Regular.ttf (embedded resources)
Resources/Accent.xaml Dynamic accent color resources
Resources/Palette.xaml Fluent Design color palette
Resources/StaticColors.xaml Non-theme-dependent color constants
Resources/Typography.xaml Font family, size, and weight resources
Resources/Variables.xaml Corner radius, spacing, sizing tokens
Resources/Wpf.Ui.xaml Master ResourceDictionary that merges all 77+ control style dictionaries
Resources/DefaultContextMenu.xaml Styled context menu for TextBox-like controls
Resources/DefaultFocusVisualStyle.xaml Fluent focus visual style

3. Control Architecture Pattern

The following class diagram shows how WPF UI controls extend WPF base classes, implement marker interfaces, and register DependencyProperties.

text
/* Detailed source-code truncated for AI context efficiency. */

Control Authoring Recipe

Every WPF UI control follows a consistent authoring pattern:

1. C# class file (Controls/{Name}/{Name}.cs):

csharp
// All controls use the flat namespace Wpf.Ui.Controls
// regardless of their folder location (ReSharper CheckNamespace suppressed)
namespace Wpf.Ui.Controls;

public class MyControl : System.Windows.Controls.ContentControl, IAppearanceControl, IIconControl
{
    // DependencyProperty registration via static readonly fields
    public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
        nameof(Icon), typeof(IconElement), typeof(MyControl),
        new PropertyMetadata(null, null, IconElement.Coerce));

    // CLR property wrapper with [Bindable] and [Category] attributes
    [Bindable(true)]
    [Category("Appearance")]
    public IconElement? Icon
    {
        get => (IconElement?)GetValue(IconProperty);
        set => SetValue(IconProperty, value);
    }

    // Static constructor: override DefaultStyleKey for implicit style resolution
    static MyControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyControl),
            new FrameworkPropertyMetadata(typeof(MyControl)));
    }
}

2. XAML style file (Controls/{Name}/{Name}.xaml):

xml
<ResourceDictionary ...>
    <Style TargetType="{x:Type controls:MyControl}">
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="Background" Value="{DynamicResource ControlFillColorDefaultBrush}" />
        
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:MyControl}">
                    
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3. Registration in master dictionary (Resources/Wpf.Ui.xaml):

xml
<ResourceDictionary.MergedDictionaries>
    
    <ResourceDictionary Source="pack://application:,,,/Wpf.Ui;component/Controls/MyControl/MyControl.xaml" />
</ResourceDictionary.MergedDictionaries>

Key invariants:

  • OverridesDefaultStyle="True" and SnapsToDevicePixels="True" are set on every control style.
  • Theme-dependent brushes always use DynamicResource (not StaticResource) so they update when themes switch at runtime.
  • The static constructor calling DefaultStyleKeyProperty.OverrideMetadata ensures WPF resolves the implicit style from the control's assembly rather than the application.
  • Complex controls split into partial classes by concern (properties, events, navigation logic, template parts).

4. Key Cross-Cutting Patterns

Flat Namespace

All controls reside in the single namespace Wpf.Ui.Controls despite being organized into individual subfolders under Controls/. This is enforced via // ReSharper disable once CheckNamespace pragmas in each file. The project does not suppress IDE0130 (namespace does not match folder structure) at the project level; instead, the ReSharper-specific pragma handles it.

Rationale: Consumers use a single xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" namespace in XAML. A flat C# namespace mirrors the flat XAML namespace.

Static Singleton Managers for Theming

The four core theming classes (ApplicationThemeManager, ApplicationAccentColorManager, SystemThemeWatcher, WindowBackgroundManager) are all static classes. They operate on Application.Current.Resources directly.

Trade-off: This sacrifices testability and multi-window isolation in favor of a simple, discoverable API. Consumers call ApplicationThemeManager.Apply(ApplicationTheme.Dark) without needing DI or service resolution. The ThemeService wraps these statics behind IThemeService for consumers who prefer DI.

The Gallery demo app and sample applications use CommunityToolkit.Mvvm source generators:

  • [ObservableProperty] for bindable properties
  • [RelayCommand] for ICommand implementations
  • ViewModels extend ObservableObject and implement INavigationAware
  • Pages implement INavigableView<TViewModel> for view-model association

This is a consumption pattern only; the core Wpf.Ui library has no dependency on CommunityToolkit.Mvvm.

Central Package Management

All NuGet package versions are declared in Directory.Packages.props at the repository root. Individual .csproj files reference packages without version numbers. This ensures consistent versions across all projects in the solution.

Service Interface Inversion

Service interfaces (INavigationService, IContentDialogService, etc.) are defined at the Wpf.Ui assembly root level, alongside their implementations. This allows:

  1. Direct instantiation for simple apps: var service = new NavigationService();
  2. DI registration for hosted apps: services.AddSingleton();
  3. The ControlsServices.Initialize(IServiceProvider) static method enables control-level service resolution.

Async Dialog Pattern

ContentDialog.ShowAsync() returns Task<ContentDialogResult> using TaskCompletionSource to bridge the WPF event model to async/await. Supports CancellationToken and closing cancellation via ContentDialogClosingEventArgs.Cancel = true.

Win32 Interop Layering

Native Windows API calls follow a strict three-layer pipeline:

text
NativeMethods.txt  -->  CsWin32 source generator  -->  Windows.Win32 namespace (auto-generated)
                                                              |
                                                    Interop/UnsafeNativeMethods.cs (handle validation)
                                                              |
                                                    Win32/Utilities.cs (OS version guards)

Manual [DllImport] in Interop/PInvoke.cs supplements CsWin32 for signatures it cannot generate (e.g., SetWindowLongPtr with nint parameters).


5. Target Framework Matrix

mermaid
gantt
    title Target Framework Coverage by Module
    dateFormat X
    axisFormat %s

    section Wpf.Ui.Abstractions
    netstandard2.0   :a1, 0, 1
    netstandard2.1   :a2, 1, 2
    net462           :a3, 2, 3
    net8.0           :a4, 3, 4
    net9.0           :a5, 4, 5
    net10.0          :a6, 5, 6

    section Wpf.Ui
    net462           :b1, 0, 1
    net472           :b2, 1, 2
    net481           :b3, 2, 3
    net8.0-windows   :b4, 3, 4
    net9.0-windows   :b5, 4, 5
    net10.0-windows  :b6, 5, 6

    section Wpf.Ui.DependencyInjection
    netstandard2.0   :c1, 0, 1
    netstandard2.1   :c2, 1, 2
    net462           :c3, 2, 3
    net8.0           :c4, 3, 4
    net9.0           :c5, 4, 5
    net10.0          :c6, 5, 6

    section Wpf.Ui.Tray
    net462           :d1, 0, 1
    net472           :d2, 1, 2
    net481           :d3, 2, 3
    net8.0-windows   :d4, 3, 4
    net9.0-windows   :d5, 4, 5
    net10.0-windows  :d6, 5, 6

    section Wpf.Ui.Gallery
    net10.0-windows10.0.26100.0 :e1, 5, 6
Module Target Frameworks Windows TFM Rationale
Wpf.Ui.Abstractions netstandard2.0, netstandard2.1, net462, net8.0, net9.0, net10.0 No Maximum compatibility; no WPF or Windows dependency. Consumable by any .NET project.
Wpf.Ui net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462 Yes Requires WPF (true) and Win32 P/Invoke. Supports .NET Framework 4.6.2+ for legacy app modernization.
Wpf.Ui.DependencyInjection netstandard2.0, netstandard2.1, net462, net8.0, net9.0, net10.0 No Only depends on abstractions and MS DI interfaces. No WPF dependency.
Wpf.Ui.Tray net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462 Yes Requires WPF + Win32 Shell32 interop for tray icons.
Wpf.Ui.SyntaxHighlight net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462 Yes Requires WPF for custom control rendering.
Wpf.Ui.ToastNotifications net10.0-windows, net9.0-windows, net8.0-windows, net481, net472, net462 Yes Same targeting as core library (currently stub).
Wpf.Ui.FlaUI net10.0-windows, net9.0-windows, net8.0-windows, net481 Yes Test automation; constrained by FlaUI.Core compatibility.
Wpf.Ui.FontMapper net10.0 No Console build tool; targets latest runtime only.
Wpf.Ui.Gallery net10.0-windows10.0.26100.0 Yes Demo app targets latest .NET + latest Windows SDK for full feature demonstration.
Wpf.Ui.Extension net481 (via VSIX SDK) N/A VS 2022 VSIX extension; targets .NET Framework 4.8.1 per VSIX SDK requirements.

6. Service Registration & DI Integration

The following sequence diagram shows how WPF UI integrates with Microsoft.Extensions.DependencyInjection via the Generic Host pattern used in the Gallery and sample applications.

mermaid
sequenceDiagram
    participant App as Application Startup
    participant Host as IHostBuilder
    participant SC as IServiceCollection
    participant Ext as ServiceCollectionExtensions
    participant SP as IServiceProvider
    participant CS as ControlsServices
    participant NavView as NavigationView
    participant Provider as INavigationViewPageProvider

    App->>Host: Host.CreateDefaultBuilder()
    App->>Host: ConfigureServices(services => ...)

    Host->>SC: services.AddSingleton<INavigationService, NavigationService>()
    Host->>SC: services.AddSingleton<IContentDialogService, ContentDialogService>()
    Host->>SC: services.AddSingleton<ISnackbarService, SnackbarService>()
    Host->>Ext: services.AddNavigationViewPageProvider<DI_PageProvider>()
    Ext->>SC: Register INavigationViewPageProvider

    Host->>SC: services.AddTransient<DashboardPage>()
    Host->>SC: services.AddTransient<SettingsPage>()

    App->>Host: Build()
    Host->>SP: Create IServiceProvider

    App->>CS: ControlsServices.Initialize(serviceProvider)
    CS->>CS: Store IServiceProvider for control-level resolution

    Note over NavView,Provider: At runtime, when NavigationView needs a page:
    NavView->>Provider: GetPage(typeof(DashboardPage))
    Provider->>SP: GetRequiredService(typeof(DashboardPage))
    SP-->>Provider: DashboardPage instance
    Provider-->>NavView: Resolved page

7. Control Lifecycle

WPF UI controls follow the standard WPF element lifecycle with additional steps for Fluent Design theming and Win32 interop integration.

mermaid
stateDiagram-v2
    [*] --> Constructed: new MyControl()
    Constructed --> Constructed: Static ctor: DefaultStyleKeyProperty.OverrideMetadata()

    Constructed --> Loaded: Added to visual tree
    Loaded --> TemplateApplied: OnApplyTemplate()
    TemplateApplied --> TemplateApplied: GetTemplateChild() binds named parts

    TemplateApplied --> Themed: DynamicResource brushes resolved
    Themed --> Themed: ApplicationThemeManager.Changed → re-resolve brushes

    Themed --> Unloaded: Removed from visual tree
    Unloaded --> Loaded: Re-added to visual tree

    Unloaded --> GarbageCollected: No references remain
    GarbageCollected --> [*]

    note right of Constructed
        DependencyProperties registered
        via static readonly fields
    end note

    note right of TemplateApplied
        Controls bind template parts
        (e.g., PART_CloseButton)
        and hook event handlers
    end note

    note right of Themed
        Theme changes trigger
        DynamicResource updates
        without re-creating controls
    end note

Lifecycle Details

Phase Trigger WPF UI Actions
Constructed new / XAML parser Static constructor registers DefaultStyleKey; DependencyProperties are static
Loaded Added to visual tree Control subscribes to theme events if needed
Template Applied OnApplyTemplate() Named template parts (PART_*) resolved via GetTemplateChild()
Themed Resource resolution DynamicResource brushes resolve from current theme dictionary
Unloaded Removed from tree Event handlers and hooks should be cleaned up
GC No remaining references Standard .NET garbage collection

Conditional Compilation

The codebase uses #if directives to handle API differences across target frameworks:

Directive Usage
NET5_0_OR_GREATER Environment.OSVersion.Version vs. registry-based fallback for OS detection
NET6_0_OR_GREATER DisposeAsync for CancellationTokenRegistration
NET48_OR_GREATER or NETCOREAPP3_0_OR_GREATER IServiceProvider support in controls
NET8_0_OR_GREATER Newer framework API usage

PolySharp provides polyfills (e.g., IsExternalInit, CallerArgumentExpression, nullable attributes) so that C# 14 language features can be used across all target frameworks.


--- METRICS ---