更新客户端渲染,更新了壳

This commit is contained in:
QWQLwToo
2026-07-06 23:05:40 +08:00
parent e7dd87bf7e
commit 31d778710b
1311 changed files with 172662 additions and 1582 deletions
@@ -0,0 +1,178 @@
# Navigation System
> 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.
---
## Navigation Lifecycle
The following sequence diagram shows the complete flow from a `NavigationService.Navigate()` call through to the page being displayed with transition animations.
```mermaid
sequenceDiagram
participant Consumer as Consumer Code
participant NavService as NavigationService
participant NavView as NavigationView
participant Provider as INavigationViewPageProvider
participant Cache as Page Cache
participant Frame as Frame
participant OldPage as Old Page (INavigationAware)
participant NewPage as New Page (INavigationAware)
participant Animator as TransitionAnimationProvider
Consumer->>NavService: Navigate(typeof(MyPage))
NavService->>NavView: NavigateInternal(pageType)
NavView->>NavView: Check if same page (skip if current)
NavView->>Provider: GetPage(pageType)
Provider->>Provider: Resolve via IServiceProvider or Activator
alt NavigationCacheMode.Enabled or Required
Provider->>Cache: Check cache for pageType
Cache-->>Provider: Cached instance or null
alt Cache hit
Provider-->>NavView: Return cached page
else Cache miss
Provider->>Provider: Create new instance
Provider->>Cache: Store in cache
Provider-->>NavView: Return new page
end
else NavigationCacheMode.Disabled
Provider->>Provider: Create new instance (always)
Provider-->>NavView: Return new page
end
NavView->>OldPage: OnNavigatedFrom()
NavView->>Frame: Navigate(newPage)
Frame->>Frame: Update Content
NavView->>NewPage: OnNavigatedTo()
NavView->>Animator: ApplyTransition(frame, transition)
Animator->>Animator: Check HardwareAcceleration.RenderingTier
alt Tier >= 2 (hardware accelerated)
Animator->>Frame: Apply Storyboard (FadeIn/SlideBottom/etc.)
else Low rendering tier
Animator-->>NavView: Skip animation
end
NavView->>NavView: Push to back stack
NavView->>NavView: Update selected menu item
NavView-->>Consumer: Navigation complete
```
---
## 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
### NavigationService
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.
@@ -0,0 +1,176 @@
# 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
```
/ 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:
```
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.
@@ -0,0 +1,213 @@
# 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
```
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>
<!-- Select theme via ThemesDictionary -->
<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.
@@ -0,0 +1,216 @@
# 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
```mermaid
graph TB
subgraph "Layer 3 — High-Level Utilities & Controls"
FluentWindow["FluentWindow<br/><i>Backdrop, corner prefs</i>"]
TitleBar["TitleBar<br/><i>Custom chrome, snap layouts</i>"]
STW["SystemThemeWatcher<br/><i>WndProc hooks</i>"]
WBM["WindowBackgroundManager<br/><i>DWM backdrop effects</i>"]
AACM["ApplicationAccentColorManager<br/><i>WinRT UISettings</i>"]
TaskBar["TaskBarService<br/><i>COM ITaskbarList4</i>"]
NotifyIcon["NotifyIcon (Tray)<br/><i>Shell_NotifyIcon</i>"]
end
subgraph "Layer 2 — Managed Wrappers (Interop/)"
UNM["UnsafeNativeMethods.cs<br/><i>Handle validation + safe wrappers</i>"]
PI["PInvoke.cs<br/><i>Manual DllImport for<br/>SetWindowLongPtr (x86/x64)</i>"]
UR["UnsafeReflection.cs<br/><i>Enum/struct unsafe casting</i>"]
end
subgraph "Layer 1 — CsWin32 Source Generation"
NMT["NativeMethods.txt<br/><i>35 function/type declarations</i>"]
CsWin32["CsWin32 Generator<br/><i>Produces Windows.Win32 namespace</i>"]
DWM["DwmSetWindowAttribute<br/>DwmIsCompositionEnabled"]
User32["SetWindowLong<br/>GetWindowLong<br/>GetDpiForWindow"]
Shell32["Shell_NotifyIcon<br/>ITaskbarList4"]
end
FluentWindow --> UNM
TitleBar --> UNM
STW --> UNM
WBM --> UNM
AACM --> UNM
TaskBar --> UNM
NotifyIcon --> UNM
UNM --> CsWin32
UNM --> PI
UNM --> UR
PI --> User32
NMT --> CsWin32
CsWin32 --> DWM
CsWin32 --> User32
CsWin32 --> Shell32
style FluentWindow fill:#fff4e1,stroke:#f57f17
style TitleBar fill:#fff4e1,stroke:#f57f17
style STW fill:#e8f5e9,stroke:#2e7d32
style WBM fill:#e8f5e9,stroke:#2e7d32
style AACM fill:#e8f5e9,stroke:#2e7d32
style TaskBar fill:#e1f5ff,stroke:#0277bd
style NotifyIcon fill:#e1f5ff,stroke:#0277bd
style UNM fill:#ffebee,stroke:#c62828
style PI fill:#ffebee,stroke:#c62828
style UR fill:#ffebee,stroke:#c62828
style NMT fill:#f3e5f5,stroke:#6a1b9a
style CsWin32 fill:#f3e5f5,stroke:#6a1b9a
style DWM fill:#e0e0e0,stroke:#616161
style User32 fill:#e0e0e0,stroke:#616161
style Shell32 fill:#e0e0e0,stroke:#616161
```
---
## Three-Layer Architecture
### Layer 1: CsWin32 Source Generation
The project uses Microsoft's [CsWin32](https://github.com/microsoft/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.
```
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.