Skip to content

Toolbar

Toolbar was a confirmed catalog gap in the initial M6 audit (no WPF implementation existed at all) and was built the same day to close it. NaviusToolbar is a plain lookless ContentControl, not a ToolBar subclass (WPF’s native ToolBar brings overflow-menu/ ToolBarTray behavior this contract does not have): it walks its logical tree for any Control implementing the IToolbarItem marker interface (buttons, links, toggle items) to give the whole toolbar, including nested toggle groups, one shared roving Tab stop.

xmlns:navius="clr-namespace:Navius.Wpf.Primitives.Controls;assembly=Navius.Wpf.Primitives"
xmlns:toolbar="clr-namespace:Navius.Wpf.Primitives.Controls.Toolbar;assembly=Navius.Wpf.Primitives"
<toolbar:NaviusToolbar Orientation="horizontal" AutomationProperties.Name="Formatting">
<toolbar:NaviusToolbarButton Content="Bold" Command="EditingCommands.ToggleBold" />
<toolbar:NaviusToolbarButton Content="Italic" Command="EditingCommands.ToggleItalic" />
<navius:NaviusSeparator Orientation="Vertical" />
<toolbar:NaviusToolbarToggleGroup Type="single" Value="{Binding Alignment, Mode=TwoWay}">
<toolbar:NaviusToolbarToggleItem Value="left" Content="Left" />
<toolbar:NaviusToolbarToggleItem Value="center" Content="Center" />
<toolbar:NaviusToolbarToggleItem Value="right" Content="Right" />
</toolbar:NaviusToolbarToggleGroup>
<toolbar:NaviusToolbarLink Content="Help" Uri="https://example.com/help" />
</toolbar:NaviusToolbar>

The consumer supplies AutomationProperties.Name/LabeledBy directly (not defaulted), matching the web contract’s “consumer must supply aria-label” requirement.

Property Type Default From Description
Orientation string "horizontal" NaviusToolbar "horizontal" or "vertical"; drives the arrow-key roving axis.
Loop bool true NaviusToolbar Arrow navigation wraps past first/last when true (default).
Dir string? null NaviusToolbar ltr/rtl; falls back to FlowDirection when unset.
Uri System.Uri? null NaviusToolbarLink Navigation target, the WPF analog of the web contract’s href. Command/CommandParameter/CommandTarget come from native Button.
Type string "single" NaviusToolbarToggleGroup "single" (at most one pressed) or "multiple" (any number pressed).
Value IReadOnlyList<string> [] NaviusToolbarToggleGroup Controlled pressed set; two-way via ValueChanged.
Disabled bool false NaviusToolbarToggleGroup Disables every item in the group (combined with each item’s own Disabled via OR).
Value string "" NaviusToolbarToggleItem Identifies this item in the group’s pressed-value set.
Disabled bool false NaviusToolbarToggleItem Effective IsEnabled is Disabled || the ancestor group's own Disabled.

NaviusToolbarButton adds no properties of its own beyond IToolbarItem; it inherits NaviusButton’s full surface (soft-disabled mode, the OnClick funnel, and NaviusButtonAutomationPeer).

Event Signature Fires when
ValueChanged RoutedEventHandler NaviusToolbarToggleGroup’s pressed set changes on a toggle-item click, driven by ToggleButton.Checked/Unchecked bubbling.
Key Behavior
Left/Right (horizontal) or Up/Down (vertical) Moves focus between enabled items (button, link, or toggle item alike), honoring Loop and Dir (RTL flip for horizontal). A nested NaviusToolbarToggleGroup’s items participate in the same scan, not their own.
Home / End Jumps focus to the first/last enabled item.
Space/Enter (on a toggle item) Toggles pressed state; Space is guarded against IsRepeat so a held key does not flap, Enter auto-repeat is allowed.
Space/Enter (on a button/link) Native Button/ToggleButton activation.

NaviusToolbarAutomationPeer (a FrameworkElementAutomationPeer, not the native ToolBarAutomationPeer, since that peer belongs to a control with overflow-menu semantics this component does not have) reports AutomationControlType.ToolBar and an orientation-aware GetOrientationCore(). NaviusToolbarToggleGroup ships its own peer reporting AutomationControlType.Group. NaviusToolbarButton inherits NaviusButtonAutomationPeer via NaviusButton; NaviusToolbarToggleItem inherits the native ToggleButton peer. Both are test-exercised (28 [StaFact] tests in ToolbarTests.cs), including real routed-key-event Space/Enter activation via an HwndSource-hosted pattern rather than fabricated KeyEventArgs.

  • 5 web parts (Root/Button/Link/Separator/ToggleGroup+ToggleItem) map onto 6 WPF classes plus the IToolbarItem marker interface. NaviusToolbarSeparator has no dedicated type: it reuses the general-purpose NaviusSeparator verbatim (same precedent as Menu), so the web contract’s auto-computed aria-orientation-perpendicular-to-toolbar behavior is not reproduced; the consumer sets Orientation on the separator explicitly.
  • NaviusToolbarToggleGroup is not a subclass of NaviusToggleGroup, even though the parity doc’s own WPF strategy invited reusing it: NaviusToggleGroup’s roving-tab-stop logic would fight NaviusToolbar owning the single shared roving domain across mixed control types. Its pressed-set selection logic (single/multiple, clear-on-reclick, replace/toggle) is ported (duplicated), not inherited.
  • Known simplification: the seated Tab stop is recomputed fresh on content-change and on every roving keypress, not via per-item IsEnabledChanged subscriptions, so an item’s disabled state flipping at runtime only moves the seated Tab stop at the next roving interaction, not instantly (the same simplification NaviusRadioGroup/NaviusToggleGroup make for their own item families).
  • DefaultValue (the contract’s uncontrolled-initial-pressed-set parameter) is dropped, matching NaviusToggleGroup’s own WPF port: Value is a single plain two-way DependencyProperty, with no separate controlled/uncontrolled distinction.
  • NaviusToolbarLink has no Disabled property at all, matching the contract’s “no intrinsic disabled state… always participates in roving”; a consumer can still set native IsEnabled directly, but that is not a modeled “Disabled” concept here.

The Toolbar Gallery page rendered at the pinned commit, in each theme.

Toolbar Gallery page in the light theme

Toolbar Gallery page in the dark theme

Toolbar Gallery page in the high contrast theme