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.
ADR-0007: Windows high contrast support
Section titled “ADR-0007: Windows high contrast support”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).