Skip to content

Toggle Group

ToggleGroup is a lookless ContentControl following the same root-owns-state, discover-descendants-via-logical-tree shape as NaviusRadioGroup. NaviusToggleGroupItem derives ToggleButton directly as its own item type (not NaviusToggle), with an explicit OnKeyDown override that activates deterministically on Space/Enter key-down rather than relying on native ButtonBase timing, decoupled from the group’s own roving-focus PreviewKeyDown handler.

xmlns:togglegroup="clr-namespace:Navius.Wpf.Primitives.Controls.ToggleGroup;assembly=Navius.Wpf.Primitives"
<togglegroup:NaviusToggleGroup x:Name="Align" Value="{Binding Alignment, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<togglegroup:NaviusToggleGroupItem Value="left" Content="Left" />
<togglegroup:NaviusToggleGroupItem Value="center" Content="Center" />
<togglegroup:NaviusToggleGroupItem Value="right" Content="Right" />
</StackPanel>
</togglegroup:NaviusToggleGroup>
Property Type Default From Description
Multiple bool false NaviusToggleGroup true: many items can be pressed at once. false (default): single, radio-like; the group unchecks every other item when one becomes checked.
Value IReadOnlyList<string> [] NaviusToggleGroup The pressed set of descendant item Values.
Orientation string "horizontal" NaviusToggleGroup Axis for the roving-focus arrow keys, "horizontal" or "vertical".
RovingFocus bool true NaviusToggleGroup true: single Tab stop, arrows move roving focus. false: every enabled item is independently a tab stop, and arrow handling is skipped entirely.
Loop bool true NaviusToggleGroup Arrow navigation wraps past first/last when true; stops at the ends when false.
Dir string? null NaviusToggleGroup "rtl" (case-insensitive) mirrors horizontal arrows; falls back to FlowDirection == FlowDirection.RightToLeft when unset.
Value (item) string "" NaviusToggleGroupItem Identifies the item in the group’s pressed set.
IsChecked (inherited) bool? false System.Windows.Controls.Primitives.ToggleButton Pressed state; IsThreeState forced false.
Event Signature Fires when
ValueChanged RoutedEventHandler The group’s Value changes because an item was checked or unchecked (click or Space/Enter).
Key Behavior
Space NaviusToggleGroupItem.OnKeyDown explicitly calls OnClick() on key-down, skipped on auto-repeat to match a native web button firing Space once on key-up.
Enter Same explicit OnKeyDown path; auto-repeat is allowed, matching native Enter behavior.
ArrowRight / ArrowDown (axis and direction resolved from Orientation/Dir) Moves roving focus only, never selection, to the next enabled item (when RovingFocus=true).
ArrowLeft / ArrowUp Moves roving focus to the previous enabled item.
Home / End Moves roving focus to the first / last enabled item.

Arrow/Home/End handling is skipped entirely when RovingFocus=false; every enabled item is independently tabbable instead. All handling requires the group to be enabled.

NaviusToggleGroup ships NaviusToggleGroupAutomationPeer (a FrameworkElementAutomationPeer) reporting AutomationControlType.Group. NaviusToggleGroupItem needs no custom peer, inheriting ToggleButtonAutomationPeer’s UIA TogglePattern. The unit suite exercises the full chain with real KeyDown-driven tests: Space and Enter toggling the focused item and updating the group’s Value (Space_TogglesFocusedItem, Enter_TogglesFocusedItem), single-mode deselect-to-empty, RovingFocus=false tab-stop behavior, disabled state cascading from the group to every item, and arrow keys moving focus without touching selection.

  • A11y-audit fix carried into the port: the 2026-07-09 web audit found Space dead on ToggleGroup items even though the contract’s own keyboard table says Space/Enter should activate. This WPF port explicitly wires both via OnKeyDown rather than relying on native ButtonBase timing, so it cannot reproduce that regression. A held-Space auto-repeat flapping bug found during the same M6 pass (holding Space rapidly toggled the pressed state) was fixed by skipping OnClick on Space auto-repeat.
  • data-multiple/data-orientation/data-disabled marker attributes are dropped; the equivalent state is exposed directly as dependency properties (Multiple, Orientation, IsEnabled) for Style triggers to read.
  • Disabled is not a new property: the root’s native IsEnabled is explicitly pushed onto every item (WPF’s IsEnabled does not auto-cascade through a ContentControl’s logical Content). Known simplification versus the contract’s independent per-item Disabled || Context.Disabled: re-enabling the group re-enables every item, even one disabled for its own reasons.
  • Native GroupName-based mutual exclusion is not used; the root owns selection fully via routed-event bubbling and an explicit PreviewKeyDown handler, with WPF’s own KeyboardNavigation.DirectionalNavigation switched off.
  • Attributes splat is dropped.

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

Toggle Group Gallery page in the light theme

Toggle Group Gallery page in the dark theme

Toggle Group Gallery page in the high contrast theme