Skip to content

Architecture decisions

Architecture decision records for the WPF port live in the framework repo at docs/adr, numbered 0001 through 0007 (there is no 0005). Each one documents a decision made while porting the web Navius primitives to WPF, its rationale, and its consequences. Summaries below are ordered by number.

ADR-0001: Web form-participation parameters drop across all families

Section titled “ADR-0001: Web form-participation parameters drop across all families”

Several web Navius families expose Name/Value/Form parameters whose only job is mirroring a hidden native <input> so the component participates in an HTML form submission. WPF has no browser-level form submission model and no hidden-input DOM node to mirror a value into, so this ADR drops that entire parameter surface across every family in the port rather than faking an equivalent. Where a family separately needs a stable identifier for a non-form purpose, such as NaviusField’s Name (used by NaviusForm to key its Errors dictionary), that parameter is kept. Real submission instead goes through NaviusForm’s SubmitCommand/Submitted event.

Touches: every form-bearing family, named explicitly for OneTimePasswordField (NaviusOneTimePasswordFieldHiddenInput) and PasswordToggleField, plus Field and Form for the non-form Name carve-out.

ADR-0002: Menu/Menubar item-type duplication

Section titled “ADR-0002: Menu/Menubar item-type duplication”

Proposed status. Controls/Menu and Controls/Menubar each ship their own leaf/checkbox/radio/ separator/label item types that look like straight duplicates on the surface. A full read of both families turned up genuine, deliberately-reasoned behavioral differences instead: different event models (a bubbling cancelable RoutedEvent for Menu versus a plain EventHandler with a Cancel field for Menubar), inverted close/command semantics on activation, a different checkbox API shape (IsIndeterminate present only on Menu’s), and different radio-group coordination mechanisms (GroupName string versus an explicit NaviusMenubarRadioGroup coordinator). The decision is not to consolidate this wave, since a subclass-or-alias merge without changing either family’s public API is not achievable given these differences; a future consolidation would need to harmonize the event contract first as a deliberate breaking-version change.

Touches: Menu and Menubar.

ADR-0003: Web substrate utilities retired (WPF)

Section titled “ADR-0003: Web substrate utilities retired (WPF)”

Four web Navius families exist only to compensate for browser/Blazor substrate gaps, not to render a reusable visual primitive: NaviusSlot (attribute-splatting/asChild approximation), NaviusCspProvider (CSP nonce cascading), NaviusDirectionProvider (reading-direction cascading), and NaviusVisuallyHidden (the sr-only clip-rect technique). WPF has a native answer to each underlying problem, so all four are retired from the WPF port rather than ported literally: composition uses ControlTemplate/ContentPresenter instead of a slot component, CSP has no WPF analog to police, FlowDirection already cascades reading direction natively, and AT-only accessible names are set directly via AutomationProperties.Name or a peer’s GetNameCore() override, the same technique NaviusAccessibleIcon already uses.

Touches: Slot, CspProvider, DirectionProvider, VisuallyHidden (all retired, not ported), with AccessibleIcon named as the precedent for the accessible-name technique consumers use instead.

ADR-0004: Chart library for Navius.Wpf.Charts

Section titled “ADR-0004: Chart library for Navius.Wpf.Charts”

The v1 chart decision is a thin, token-themed wrapper over a single OSS WPF chart library, scoped so the dependency lives only in Navius.Wpf.Charts.csproj. Two MIT-licensed candidates were spiked, LiveChartsCore.SkiaSharpView.WPF (LiveCharts2) and ScottPlot.WPF; LiveCharts2 won on runtime brush/color re-theming (settable Fill/Stroke paint properties that redraw directly when reassigned, versus ScottPlot’s re-theme-by-hand Palette model) and on API fit for a line/bar/area/pie surface. Re-theming stays push-based: NaviusChart.RefreshTheme() re-resolves tokens after a consumer calls ThemeManager.Apply(...), since ThemeManager exposes no theme-changed signal to subscribe to yet.

Touches: Navius.Wpf.Charts (NaviusChart).

ADR-0006: M6 RTL + multi-monitor DPI hardening

Section titled “ADR-0006: M6 RTL + multi-monitor DPI hardening”

Two independent hardening efforts. First, NaviusAnchoredPopup positioned every popup against the primary monitor’s work area, which was wrong on a non-primary monitor; a new Positioning/MonitorWorkArea.cs resolves the containing monitor’s work area via P/Invoke, with the Gallery app also opting into dpiAwareness=PerMonitorV2. Second, an RTL audit across the catalog found one real bug (DateInput/TimeInput’s segment order was mirroring under RTL when it should stay in a fixed reading order, fixed by pinning FlowDirection="LeftToRight" on the segments panel), corrected one false-flagged doc claim (Rating’s half-fill clip does mirror correctly), and confirmed several other controls (Switch, Breadcrumb, Pagination) already mirror correctly with no code change needed. The audit was not exhaustively re-run pixel-by-pixel for every control the task named (Slider/Progress/Meter fill direction, Tabs/Accordion chevrons, Sortable), left as a residual.

Touches: popover/positioning (NaviusAnchoredPopup), DateInput, TimeInput, Rating, Switch, Breadcrumb, Pagination.

High contrast is a native-Windows accessibility obligation the web port never had. This ADR adds a third NaviusTheme.HighContrast value to ThemeManager plus Themes/Tokens.HighContrast.xaml, which maps every Navius token to a SolidColorBrush bound via DynamicResource to a SystemColors.* key so live OS palette changes repaint without an app restart. An opt-in ThemeManager.EnableSystemHighContrastSync() subscribes to SystemParameters.StaticPropertyChanged and applies or restores the high-contrast theme as the OS setting flips. The same wave’s UIA peer sweep also fixed a real gap in Tree (custom multi-select state was invisible to SelectionItemPattern because native TreeViewItem.IsSelected never reflected it) and flagged two unfixed candidates for owner follow-up, OneTimePasswordField’s container exposing no assembled value or pattern over UIA, and Combobox’s automation peer missing a GetNameCore override that its sibling Autocomplete already has.

Touches: ThemeManager (cross-cutting), Tree (fixed), OneTimePasswordField and Combobox (flagged, not fixed).