Skip to content

Menubar

Menubar is the closest 1:1 web-to-native match in this catalog: WPF’s Menu is already exactly a horizontal bar of top-level MenuItems, each of which owns its own submenu popup, roving/typeahead keyboard navigation, and hover-to-switch (“follow focus”) between open siblings, so almost the entire web contract’s Trigger/Portal/Positioner/Popup/Arrow/DismissableLayer/ RovingFocus machinery collapses onto native behavior for free. The 18-part web contract maps onto 9 WPF classes.

xmlns:menubar="clr-namespace:Navius.Wpf.Primitives.Controls.Menubar;assembly=Navius.Wpf.Primitives"
<menubar:NaviusMenubar Value="{Binding OpenMenu, Mode=TwoWay}">
<menubar:NaviusMenubarMenu Value="file" Header="File">
<menubar:NaviusMenubarItem Header="New" Command="ApplicationCommands.New" />
<menubar:NaviusMenubarCheckboxItem Header="Word wrap" Checked="{Binding WordWrap, Mode=TwoWay}" />
<menubar:NaviusMenubarSeparator />
<menubar:NaviusMenubarSubTrigger Header="Recent">
<menubar:NaviusMenubarItem Header="Document 1.txt" />
</menubar:NaviusMenubarSubTrigger>
</menubar:NaviusMenubarMenu>
<menubar:NaviusMenubarMenu Value="edit" Header="Edit">
<menubar:NaviusMenubarItem Header="Copy" Command="ApplicationCommands.Copy" />
</menubar:NaviusMenubarMenu>
</menubar:NaviusMenubar>

A labeled group of items is expressed the idiomatic native way: a NaviusMenubarLabel followed by its items followed by a NaviusMenubarSeparator, all as flat siblings (there is no NaviusMenubarGroup wrapper type). A mutually-exclusive radio set is coordinated through a non-visual NaviusMenubarRadioGroup resource rather than nesting:

<menubar:NaviusMenubarRadioGroup x:Key="ViewGroup" Value="list" />
...
<menubar:NaviusMenubarRadioItem Value="list" Group="{StaticResource ViewGroup}" Header="List" />
<menubar:NaviusMenubarRadioItem Value="grid" Group="{StaticResource ViewGroup}" Header="Grid" />
Property Type Default From Description
Value string? null NaviusMenubar Controlled open-menu value; two-way bindable. Set from the child NaviusMenubarMenu.Value on submenu open/close.
Orientation Orientation Horizontal NaviusMenubar Horizontal (native Menu layout) or Vertical (swaps ItemsPanel to a StackPanel).
Dir string? null NaviusMenubar ltr/rtl; overrides inherited FlowDirection when set.
Loop bool false NaviusMenubar When false, arrow-key navigation across top-level triggers clamps at the ends instead of wrapping (best-effort, see Web deltas).
Modal bool true NaviusMenubar API-parity only; native per-submenu Popup dismissal already scopes outside-press/Escape, so there is no additional behavior.
Value string? null NaviusMenubarMenu Identity of this menu; throws InvalidOperationException in OnInitialized if null/empty.
TextValue string? null NaviusMenubarItem Kept for API parity; native typeahead already matches Header text and is not wired to this property.
Checked bool? null NaviusMenubarCheckboxItem Two-way bindable tri-state; null = indeterminate, mirrored one-way into native IsChecked (indeterminate maps to false).
Value string? null NaviusMenubarRadioItem Throws InvalidOperationException in OnInitialized if null/empty.
Group NaviusMenubarRadioGroup? null NaviusMenubarRadioItem The coordinator this item participates in.
Value string? null NaviusMenubarRadioGroup Selected value shared across every RadioItem pointed at this group; a non-visual DependencyObject, not part of the visual/logical tree.
Dir string? null NaviusMenubarSubTrigger Overrides cascaded FlowDirection for this submenu.
Event Signature Fires when
ValueChanged RoutedEventHandler NaviusMenubar.Value changes, driven by the child menus’ SubmenuOpened/SubmenuClosed cascade.
Select EventHandler<NaviusMenubarSelectEventArgs> An item/checkbox item/radio item is activated, before the native click; cancelable via PreventDefault().
CheckedChanged EventHandler<bool?> NaviusMenubarCheckboxItem.Checked changes, raised from the DP-change callback so it fires on every change, programmatic or click-driven.
ValueChanged EventHandler<string?> NaviusMenubarRadioGroup.Value changes, including from a RadioItem selection.
OpenChanged RoutedEventHandler NaviusMenubarSubTrigger’s submenu opens or closes, forwarded from native SubmenuOpened/SubmenuClosed.
Key Behavior
ArrowLeft / ArrowRight (trigger row, no menu open) Native roving across top-level MenuItems; clamped instead of wrapped when Loop=false via a PreviewKeyDown interceptor (best-effort, see Web deltas).
Home / End (trigger row) Native (inherited).
ArrowDown / ArrowUp / Enter / Space (on a trigger, its menu closed) Opens that menu (native MenuItem submenu-open behavior).
Hovering a different trigger while another menu is open Native “follow focus”: that trigger’s menu opens automatically.
ArrowDown / ArrowUp (inside an open menu) Native roving between items.
ArrowRight (ArrowLeft in rtl) / Enter / Space (on a submenu header) Opens the submenu (native).
ArrowLeft (ArrowRight in rtl) (inside a submenu) Closes only that submenu (native).
Escape Closes the open (sub)menu (native Popup dismissal).

NaviusMenubarAutomationPeer derives from MenuAutomationPeer, overriding only GetClassNameCore(); the automation control type and patterns it reports are otherwise whatever the native MenuAutomationPeer provides. NaviusMenubarLabel ships its own peer (a MenuItemAutomationPeer subclass) reporting AutomationControlType.Text with IsControlElementCore() true, since it is a non-interactive heading, not a real menu item. Ordinary items, checkbox items, and radio items inherit MenuItemAutomationPeer. The unit suite exercises the Value-required throws, the Loop=false clamp, and the CheckedChanged-raises-on- programmatic-set regression added at M6.

  • 18 web parts collapse onto 9 WPF classes: NaviusMenubarMenu absorbs Trigger+Portal+ Positioner+Popup+Arrow (its Header renders as the trigger, its Items as the floating, dismissable popup); NaviusMenubarSubTrigger absorbs Sub+SubTrigger+SubContent via native MenuItem nesting.
  • NaviusMenubarGroup has no wrapper type: express a labeled group as a NaviusMenubarLabel followed by its items followed by a NaviusMenubarSeparator, all flat siblings (wrapping in an arbitrary ItemsControl would make WPF auto-generate a blank container MenuItem).
  • NaviusMenubarRadioGroup does not nest its items either, for the same reason: it is a non-visual DependencyObject coordinator declared as a resource and pointed at by each RadioItem.Group, rather than an ItemsControl wrapper.
  • CheckboxItem/RadioItem never call base.OnClick(): native MenuItem’s own IsCheckable click handling would double-toggle IsChecked against this port’s own tri-state/Group-driven logic, so both raise Click manually instead. Net effect: a bound Command on either type never executes (an accepted OnSelect-driven delta, not a regression).
  • Loop=false is best-effort: native WPF top-level menu navigation always wraps; false is implemented as a PreviewKeyDown interceptor that swallows the wrapping keypress, not a first-class navigation-engine setting.
  • Every stateful DP (Value, Checked, RadioGroup.Value) is a plain two-way DependencyProperty whose paired event always raises on change; this collapses the web contract’s split controlled/uncontrolled (HasDelegate vs. explicit-SetParametersAsync- tracking) strategies into one canonical model.
  • Captured unmatched attributes (Attributes) are not ported anywhere in this family; there is no Blazor-style unmatched-parameter bag in WPF.
  • Known residual (M6 audit): NaviusMenubar.Value’s own DP-change callback does not itself raise ValueChanged; the event only fires through the SubmenuOpened/SubmenuClosed cascade, so a programmatic Value change in a headless context (or when the target menu is already open) may not raise it, even though every other DP in this family “always raises on change.”
  • Known residual (M6 audit): bound Command/CommandParameter on NaviusMenubarCheckboxItem/ NaviusMenubarRadioItem never executes, for the reason above; documented as an accepted delta, not a regression.

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

Menubar Gallery page in the light theme

Menubar Gallery page in the dark theme

Menubar Gallery page in the high contrast theme