Skip to content

Navigation Menu

NavigationMenu is a Tier B custom control set for a non-modal, hover/keyboard-disclosed site-navigation row: no native WPF control models this pattern, so it is built on ContentControl/ItemsControl with a NaviusAnchoredPopup-hosted content panel per open item. This port implements only per-item popup mode; the web contract’s shared/morphing viewport (one popup that resizes and re-anchors as the active item changes) is a stub that throws NotSupportedException when requested.

xmlns:navigationmenu="clr-namespace:Navius.Wpf.Primitives.Controls.NavigationMenu;assembly=Navius.Wpf.Primitives"
<navigationmenu:NaviusNavigationMenu Value="{Binding OpenItem, Mode=TwoWay}">
<navigationmenu:NaviusNavigationMenuList>
<navigationmenu:NaviusNavigationMenuItem Value="products">
<navigationmenu:NaviusNavigationMenuTrigger Content="Products" />
<navigationmenu:NaviusNavigationMenuContent>
<StackPanel>
<navigationmenu:NaviusNavigationMenuLink Content="Overview" Href="/products" />
<navigationmenu:NaviusNavigationMenuLink Content="Pricing" Href="/pricing" />
</StackPanel>
</navigationmenu:NaviusNavigationMenuContent>
</navigationmenu:NaviusNavigationMenuItem>
<navigationmenu:NaviusNavigationMenuItem Value="docs">
<navigationmenu:NaviusNavigationMenuLink Content="Docs" Href="/docs" />
</navigationmenu:NaviusNavigationMenuItem>
</navigationmenu:NaviusNavigationMenuList>
</navigationmenu:NaviusNavigationMenu>

NaviusNavigationMenuSub (nested submenu-in-a-panel) shares the same Value/Orientation/ Delay/CloseDelay host surface as the root (both derive from NavigationMenuHostBase), but defaults Orientation to "vertical" and renders as a plain ContentControl, not a <nav> landmark equivalent.

Property Type Default From Description
Value string? null NavigationMenuHostBase Value of the currently open item; two-way bindable. null = nothing open.
Orientation string "horizontal" NavigationMenuHostBase "horizontal" or "vertical"; drives the List’s roving-focus axis. NaviusNavigationMenuSub defaults this to "vertical" in its constructor.
Delay int 50 NavigationMenuHostBase Milliseconds before hover opens an item’s content.
CloseDelay int 50 NavigationMenuHostBase Milliseconds before leaving the menu closes open content.
UseSharedViewport bool false NaviusNavigationMenu Requests the shared/morphing-viewport layout; not implemented, throws NotSupportedException when set true.
Value string "" NaviusNavigationMenuItem Matched against the ambient host’s open Value.
NativeButton bool true NaviusNavigationMenuTrigger Render-contract parity flag only; this is always a native Button, so it has no behavioral effect.
IsOpen bool - NaviusNavigationMenuIcon Read-only. Mirrors the owning item’s open state for a template trigger (rotate chevron).
Href string? null NaviusNavigationMenuLink Link target; informational only, no navigation side effect authored here.
Active bool false NaviusNavigationMenuLink Marks the current page: sets AutomationProperties.ItemStatus="page".
ForceMount bool false NaviusNavigationMenuContent Keep the panel mounted while closed; accepted for API parity, no exit-animation phase exists to preserve.
Side PlacementSide Bottom NaviusNavigationMenuContent Positioning knob forwarded to the hosted NaviusAnchoredPopup.
Align PlacementAlign Center NaviusNavigationMenuContent -
SideOffset double 0 NaviusNavigationMenuContent -
AlignOffset double 0 NaviusNavigationMenuContent -
IsOpen bool - NaviusNavigationMenuContent Read-only. True while this panel is the open one.
Width / Height double 10 / 5 NaviusNavigationMenuArrow Reuses FrameworkElement’s own Width/Height, metadata-overridden.
KeepMounted bool false NaviusNavigationMenuBackdrop Keep the backdrop mounted while closed.
ForceMount bool false NaviusNavigationMenuViewport Accepted for API parity only; inert stub, only meaningful in shared-viewport mode (not implemented).
Method Description
RequestOpen(string value) Opens the given item if it is not already open.
RequestClose(string value) Closes the given item if it is the currently open one.
Toggle(string value) Opens the item if closed, closes it if already open.
RequestOpenViaKeyboard(string value) Opens the item and marks it for the APG “enter content” pattern: its Content panel moves focus to its first focusable descendant once open.

All four live on NavigationMenuHostBase, shared by NaviusNavigationMenu and NaviusNavigationMenuSub.

Event Signature Fires when
ValueChanged EventHandler<string?> Value changes, for any reason.
OpenChangeComplete EventHandler<bool> Fires synchronously right after Value changes; this port has no separate presence-transition phase to wait for.
Select EventHandler<NaviusNavigationMenuSelectEventArgs> NaviusNavigationMenuLink is activated, before the native click; cancelable via PreventDefault() (Click still raises).
Key Behavior
Tab The trigger row is a single roving-tabindex seat: only the open item’s trigger (or the first) is in the Tab order.
ArrowLeft/ArrowRight (horizontal) or ArrowUp/ArrowDown (vertical) NaviusNavigationMenuList’s own PreviewKeyDown roves focus across Trigger/Link descendants.
Home / End Moves focus to the first/last rovable trigger or link.
Space / Enter (on Trigger) Toggles this item’s content.
ArrowDown (Trigger, orientation=horizontal) / ArrowRight (Trigger, orientation=vertical) “Enter content”: opens the item and moves focus to the first focusable descendant of its panel.
Escape Closes the open item via a window-level PreviewKeyDown hook, or NaviusNavigationMenuContent’s own local handler when focus is inside that specific popup; focus returns to the trigger.

NaviusNavigationMenuTrigger ships a custom NaviusNavigationMenuTriggerAutomationPeer (a ButtonAutomationPeer implementing IExpandCollapseProvider), reflecting and driving the host’s Value; test-exercised. NaviusNavigationMenuBackdrop reuses OverlayBackdrop but registers its own DefaultStyleKey (a subclass that skips this does not reliably resolve the base type’s implicit style). The root (NaviusNavigationMenu/Sub) and the Content panel ship no custom automation peer; their AutomationControlType is left as the default FrameworkElementAutomationPeer inference, pending a cross-family consistency decision. NaviusNavigationMenuLink maps Active to AutomationProperties.ItemStatus="page" as an approximation of aria-current (no exact UIA equivalent).

  • Scope: only per-item popup mode is implemented. UseSharedViewport=true throws NotSupportedException immediately rather than faking the morph animation; NaviusNavigationMenuViewport is a present-but-inert stub, only meaningful in that unimplemented mode.
  • The contract’s NaviusNavigationMenuPopup/Positioner/Portal collapse onto NaviusNavigationMenuContent directly: its default template hosts a NaviusAnchoredPopup, with Side/Align/SideOffset/AlignOffset as the only positioning knobs the substrate exposes. Not implemented: Flip/AvoidCollisions as independent toggles (the substrate always flips and shifts), CollisionPadding, Sticky, HideWhenDetached, ArrowPadding, Container (moot; WPF’s Popup already renders outside the normal visual flow).
  • Captured unmatched attributes (Attributes) are not ported anywhere in this family.
  • data-activation-direction (a directional enter-animation cue) is not implemented.
  • Known residual (M6 audit): dismissal is window-hook-based (PreviewMouseDown/ PreviewKeyDown), which cannot see presses landing inside another host’s own open Content panel (a separate, transparent child HWND); a press inside one host’s popup while a different host’s (or a nested Sub’s) popup is open will not auto-close the other. Traces into NaviusAnchoredPopup’s separate-HWND rendering, outside this family’s edit boundary.
  • NaviusNavigationMenuLink.Active maps to AutomationProperties.ItemStatus="page" as a defensible but approximate stand-in for aria-current="page" (no exact UIA equivalent); noted for cross-family consistency review.

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

Navigation Menu Gallery page in the light theme

Navigation Menu Gallery page in the dark theme

Navigation Menu Gallery page in the high contrast theme