Skip to content

Architecture decisions

Architecture decision records for the WPF port live in the framework repo at docs/adr, numbered 0001 through 0008 (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. NaviusChart subscribes to ThemeManager.ThemeChanged while loaded and re-resolves its token palette automatically. RefreshTheme() remains available for scoped resource changes that do not go through the application theme manager.

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).

ADR-0008: Native reduced-motion execution policy

Section titled “ADR-0008: Native reduced-motion execution policy”

Navius.Wpf.Motion previously carried reduced-motion metadata but both WPF executors always ran the full spring. MotionPolicy.System now reads SystemParameters.ClientAreaAnimation, while an injectable Func<bool> constructor gives apps and tests a deterministic preference. Reduced SpringTicker runs complete synchronously without attaching CompositionTarget.Rendering; reduced baked animations contain one target keyframe at time zero. MotionPolicy.Resolve applies MicroReduce.Collapse and MicroReduce.OpacityOnly to micro preset frames.

The styled layer keeps its own equivalent seam because vendored controls cannot acquire a Motion package dependency without breaking source-closure distribution. Carousel uses that seam to choose between a 150ms fade and an immediate visibility swap, and inactive slides become Collapsed so their descendants leave UI Automation.

Touches: Navius.Wpf.Motion, Carousel, Gallery UIA coverage.