# 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}/`: ``` 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 11,5,11,6 ``` ### 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 /// Identifies the dependency property. 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 ### Navigation 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 `` 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 ``` 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](https://docs.microsoft.com/dotnet/desktop/wpf/controls/control-authoring-overview)