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