Back to Wpfui

WPF UI Project Constitution v4.2.0

docs/architecture/CONSTITUTION.md

4.3.033.2 KB
Original Source

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

FieldValue
NameWPF UI (wpfui)
Version4.2.0
TypeOpen-source WPF UI control library implementing Microsoft Fluent Design System
LicenseMIT
LanguageC# 14 (LangVersion: preview) / XAML
Platforms.NET 10/9/8 (windows TFMs) + .NET Framework 4.6.2/4.7.2/4.8.1
NuGet Package IDWPF-UI
XAML Namespacehttp://schemas.lepo.co/wpfui/2022/xaml (prefix ui)
Repositoryhttps://github.com/lepoco/wpfui
FormatterCSharpier (110 char width, 4-space indent, no tabs)
AnalyzersStyleCop, AsyncFixer, IDisposableAnalyzers, WpfAnalyzers (all enforced in build)
Test FrameworksXUnit v3, NSubstitute, AwesomeAssertions, FlaUI, Coverlet
Assembly Signingsrc/lepo.snk for release builds

Project Structure

ProjectPurposeTFMs
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 abstractionsIncludes netstandard2.0/2.1
src/Wpf.Ui.DependencyInjection/Microsoft.Extensions.DependencyInjection integrationMulti-target
src/Wpf.Ui.Tray/System tray supportMulti-target
src/Wpf.Ui.ToastNotifications/Toast notification supportMulti-target
src/Wpf.Ui.Gallery/Demo/showcase applicationnet10.0-windows10.0.26100.0
src/Wpf.Ui.Extension/Visual Studio 2022 extension with project templatesVS SDK
samples/Sample apps (MVVM, DI, console hosting)Various

Core Library Layout (src/Wpf.Ui/)

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

    Based on Microsoft XAML for Win UI
    Copyright (c) Microsoft Corporation. All Rights Reserved.
-->

2.5 XML Documentation

Public APIs require <summary> 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 <summary>:

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 ClassUse For
System.Windows.Controls.ContentControlCard, Badge, InfoBar, Snackbar
System.Windows.Controls.ButtonButton
System.Windows.Controls.ControlTitleBar, ProgressRing, NavigationView
System.Windows.Controls.Primitives.ToggleButtonToggleSwitch
System.Windows.WindowFluentWindow

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

    <!-- Constants use StaticResource -->
    <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:

TypePatternExample
Brushes{Name}BrushButtonForeground, TextFillColorPrimaryBrush
Colors{Name}ColorAccentColor
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:

InterfaceImplementationPurpose
INavigationServiceNavigationServicePage navigation
IContentDialogServiceContentDialogServiceModal content dialogs
ISnackbarServiceSnackbarServiceToast-like snackbar notifications
ITaskBarServiceTaskBarServiceWindows taskbar integration
IThemeServiceThemeServiceTheme 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)

TypeUse For
featNew feature
fixBug fix
choreBuild/tooling changes
ScopeUse For
controlsControl-related changes
appGallery app changes
docsDocumentation changes

Examples from actual commits:

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.

#RuleEnforcement
A1Use file-scoped namespaces in all C# files.editorconfig csharp_style_namespace_declarations = file_scoped:warning
A2Enable nullable reference typesDirectory.Build.props <Nullable>enable</Nullable>
A3Add MIT license file header to every .cs and .xaml file.editorconfig IDE0073 = error
A4Add XML docs with <summary> and <example> tags for all public APIsCode review
A5Use CSharpier for formatting (110 char, 4-space indent).csharpierrc, CI
A6Use Central Package Management (versions in Directory.Packages.props only)Directory.Build.props <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
A7Validate Win32 handles before any native API call (IntPtr.Zero + PInvoke.IsWindow)Code review
A8Use pattern matching (is not) over type checks with casts.editorconfig csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
A9Use DependencyProperty.Register with nameof()Code review, WpfAnalyzers
A10Include XAML example in XML docs for controlsCode review
A11Follow commit convention: type(scope): description (#PR_NUMBER)Code review
A12Search existing codebase before assuming standard WPF approaches work for Win32Code review
A13Use _camelCase for private fields.editorconfig naming rules
A14Set OverridesDefaultStyle=True and SnapsToDevicePixels=True in control stylesCode review
A15Use DynamicResource for theme-dependent values, StaticResource for constantsCode review
A16Place each control in its own subfolder under Controls/Project convention

3.2 ASK FIRST

These actions require discussion with the maintainer before proceeding.

#ActionReason
Q1Adding a new NuGet package dependencyImpacts all consumers; increases dependency surface area
Q2Modifying Win32/Interop layerMay affect multiple OS versions; requires testing on Windows 10/11
Q3Changing public API surfaceBreaking change potential for library consumers
Q4Modifying TitleBar or window management codeComplex Win32 interaction with WndProc message handling, snap layouts, DWM
Q5Adding new multi-target framework conditional compilationIncreases maintenance burden across 6 TFMs
Q6Modifying theme/accent color managementSystem-wide effects; affects all controls
Q7Changing NavigationView behaviorMost complex control in the library (27+ DependencyProperties, 7+ RoutedEvents, 6 partial files)
Q8Modifying assembly signing or packaging configurationImpacts NuGet distribution and strong-name consumers

3.3 NEVER DO

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

#RuleEnforcement
N1Never add package versions directly to .csproj filesCentral Package Management enabled
N2Never use var for non-apparent types.editorconfig csharp_style_var_elsewhere = false:warning
N3Never use primary constructors.editorconfig IDE0290 = none (suppressed)
N4Never add comments that merely restate what the code doesCode review
N5Never use emoticons in code or commentsCode review
N6Never assume library/NuGet availability without checking Directory.Packages.propsCode review
N7Never skip handle validation in Win32 interop codeCode review
N8Never use expression-bodied members for constructors, methods, or properties.editorconfig csharp_style_expression_bodied_* = false:silent
N9Never modify namespace to match folder structure for Controls.editorconfig IDE0130 = none (suppressed); flat Wpf.Ui.Controls namespace
N10Never 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

FileLocationPurpose
.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 TypeIndentSize
C# (*.cs)Spaces4
XML/JSON/YAML (*.xml, *.json, *.yml)Spaces2
MSBuild (*.csproj, *.props, *.targets)Spaces2
Solutions (*.sln, *.slnx)Tabs4
Markdown (*.md)Spaces2

Enforced Diagnostics (error/warning level):

DiagnosticSeverityDescription
IDE0073errorFile header template (MIT license)
csharp_style_namespace_declarationswarningFile-scoped namespaces
csharp_style_var_elsewherewarningNo var for non-apparent types
dotnet_style_predefined_type_for_locals_parameters_memberswarningLanguage keywords over BCL types
dotnet_style_readonly_fieldwarningReadonly fields where possible
csharp_prefer_static_local_functionwarningStatic local functions
csharp_style_pattern_local_over_anonymous_functionwarningLocal functions over lambdas
csharp_using_directive_placementwarningUsings outside namespace

Suppressed Diagnostics:

DiagnosticReason
SA1101No this. qualification required
SA1309Allow underscore-prefixed field names
SA1600Relaxed XML documentation enforcement (not all members require docs)
CS1591Missing XML comment (15000+ existing warnings)
IDE0130Namespace not matching folder structure (flat Wpf.Ui.Controls)
IDE0290Primary constructors not used
CA1510ArgumentNullException.ThrowIfNull helper not available on older TFMs
WPF0070-0073ValueConversion attribute warnings relaxed

Naming Rules:

SymbolStyleExample
ConstantsPascalCaseMaxRetryCount
Public/internal/protected fieldsPascalCaseDefaultTimeOut
Public static readonly fieldsPascalCaseIconProperty
Private/protected fields_camelCase_presenter
Public methods/properties/eventsPascalCaseNavigate()
ParameterscamelCasecontentPresenter
InterfacesI + PascalCaseISnackbarService

.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

PropertyValueEffect
Version4.2.0Assembly and package version
LangVersion14.0C# 14 language features
NullableenableNullable reference types globally enabled
AllowUnsafeBlockstrueRequired for Win32 interop
ManagePackageVersionsCentrallytrueCentral Package Management
EnforceCodeStyleInBuildtrueAnalyzer warnings fail the build
GenerateDocumentationFiletrueFor core projects only
PackageLicenseExpressionMITLicense 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:

PackageVersionPurpose
CommunityToolkit.Mvvm8.4.0MVVM toolkit for Gallery/samples
Microsoft.Windows.CsWin320.3.242P/Invoke source generator
StyleCop.Analyzers1.2.0-beta.556Code style analyzer
WpfAnalyzers4.1.1WPF-specific analyzers
AsyncFixer1.6.0Async/await analyzer
IDisposableAnalyzers4.0.8IDisposable pattern analyzer
xunit.v33.2.0Unit test framework
NSubstitute5.3.0Mocking framework
AwesomeAssertions9.3.0Fluent assertions
FlaUI.Core / FlaUI.UIA35.0.0UI automation testing
PolySharp1.15.0C# feature polyfills for older TFMs
Microsoft.Extensions.Hosting10.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

FieldValue
Library Version4.2.0
Language VersionC# 14 (preview)
Target Frameworks.NET 10/9/8, .NET Framework 4.8.1/4.7.2/4.6.2
NuGet Package IDWPF-UI
XAML Namespacehttp://schemas.lepo.co/wpfui/2022/xaml
XAML Prefixui
Constitution Version1.0 (2025-02-10)