Skip to content

Drawer

Drawer is a lookless ContentControl docked to one edge (Side) of the window and driven by the same NaviusOverlaySurfaceBase state machine as Dialog and AlertDialog: open/close, Escape and optional outside-click dismissal, and focus cycling through the shared OverlayStack. On top of that it slides its panel in and out from the docked edge. This port is keyboard and button dismiss only: drag-to-dismiss, snap points, and the web contract’s swipe-progress hooks are not implemented.

xmlns:navius="clr-namespace:Navius.Wpf.Primitives.Controls;assembly=Navius.Wpf.Primitives"
xmlns:overlay="clr-namespace:Navius.Wpf.Primitives.Controls.OverlaySurface;assembly=Navius.Wpf.Primitives"
xmlns:drawer="clr-namespace:Navius.Wpf.Primitives.Controls.Drawer;assembly=Navius.Wpf.Primitives"
<Grid>
<!-- app content -->
<navius:NaviusButton Content="Open filters" Click="OnOpenDrawer" />
<!-- one layer per window, stretched over the root -->
<overlay:NaviusOverlayLayer>
<drawer:NaviusDrawer x:Name="Filters"
Side="Right"
Title="Filters"
Description="Narrow the results.">
<StackPanel>
<TextBlock Text="Filter options go here." />
<navius:NaviusButton Content="Close"
Command="overlay:NaviusOverlaySurfaceBase.CloseCommand" />
</StackPanel>
</drawer:NaviusDrawer>
</overlay:NaviusOverlayLayer>
</Grid>

Like Dialog, a Drawer is declared as a direct XAML child of the layer and stays Collapsed until opened; a window with no NaviusOverlayLayer still parses, but opening logs a Trace.TraceWarning and reverts IsOpen to false. The default PART_Panel template part is what slides; a custom template must name a panel element the same way for the slide animation to find it (a 360px fallback extent applies if the panel has no explicit Width/Height set on its offset axis).

Property Type Default From Description
IsOpen bool false NaviusOverlaySurfaceBase Open state. Two-way bindable by default. A canceled Closing reverts it to true so the DP never desyncs from the live overlay.
Title string? null NaviusOverlaySurfaceBase Accessible name. Applied via AutomationProperties.Name on engage.
Description string? null NaviusOverlaySurfaceBase Accessible help text. Applied via AutomationProperties.HelpText on engage.
Modal bool true NaviusDrawer When true: focus is trapped, outside interaction is blocked. When false: no trap, outside content stays interactive.
CloseOnOutsideClick bool true NaviusDrawer Whether a press outside the panel dismisses the drawer.
Side NaviusDrawerSide Bottom NaviusDrawer The edge the sheet docks to and slides from: Left | Right | Top | Bottom. An enum, replacing the web contract’s unvalidated free string.
Content object - ContentControl The drawer body.
Method Description
Open() Sets IsOpen to true via SetCurrentValue, preserving an existing two-way binding.
Close() Sets IsOpen to false via SetCurrentValue, preserving an existing two-way binding.
Command Description
NaviusOverlaySurfaceBase.CloseCommand Bind any element inside Content to request a close, mirroring the web’s NaviusDrawerClose part.
Event Signature Fires when
Opened EventHandler The overlay has engaged: pushed onto the OverlayStack, slide-and-fade enter animation started.
Closing EventHandler<OverlayClosingEventArgs> A close was requested (Escape, outside press, CloseCommand, or IsOpen = false). Cancelable: set Cancel to keep the drawer open.
Closed EventHandler The exit animation completed and the surface left its layer.
Key Behavior
Escape Closes the drawer. Fixed on, not configurable.
Tab / Shift + Tab Cycles focus among the drawer’s focusable descendants while Modal is true, via KeyboardNavigationMode.Cycle applied by the OverlayStack focus trap.

NaviusDrawerAutomationPeer (a FrameworkElementAutomationPeer) reports AutomationControlType.Window and overrides IsDialogCore() to return true, the same mapping as Dialog: a docked sheet is still, semantically, a dialog to assistive tech. Title/Description are applied as AutomationProperties.Name/HelpText on engage, the same shared NaviusOverlaySurfaceBase.Engage() behavior Dialog and AlertDialog use (applied uniformly across all three families even though the web source’s own missing-title dev warning only exists on Dialog’s Popup). The unit suite (DrawerTests.cs) exercises the shared canceled-Closing fix, the Side enum, and DrawerGeometry.GetOffscreenOffset’s per-side offset math directly, without a live visual tree or running Storyboard.

  • One control instead of eight parts, same shape as Dialog: the web’s Root/Trigger/Portal/ Backdrop/Popup/Title/Description/Close anatomy folds into NaviusDrawer plus CloseCommand.
  • Side is the NaviusDrawerSide { Left, Right, Top, Bottom } enum, not a free string, resolving the web contract’s own open question about needing a proper type. The per-side offscreen offset is computed by the pure, unit-tested DrawerGeometry.GetOffscreenOffset(Side, panelSize).
  • Drag-to-dismiss (createSheetSwipe), snap points, and the data-swiping/ --drawer-swipe-movement-x/y swipe-progress hooks are not implemented: this port is keyboard (Escape) and button (CloseCommand) dismiss only.
  • NaviusOverlayLayer replaces the web’s CSS-selector Container parameter, the same as Dialog.
  • Enter/exit is a 150ms Opacity fade (inherited) plus a TranslateTransform slide of PART_Panel between DrawerGeometry.GetOffscreenOffset(Side, extent) and (0, 0), driven by plain DoubleAnimations; Navius.Wpf.Primitives does not reference Navius.Wpf.Motion.
  • The web source’s dev-time “missing title” warning exists only on Dialog’s Popup; the WPF port closes that inconsistency by applying the same AutomationProperties.SetName/SetHelpText treatment uniformly to Dialog, AlertDialog, and Drawer via the shared base’s Engage().
  • Known residual (M6 audit): a non-modal Drawer (Modal="false") does not restore focus to the previously focused element on close, the same root cause as Dialog’s residual (OverlayStack only captures a restore target when TrapFocus is true, which tracks modality for every current consumer). Left for the Overlays owner.

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

Drawer Gallery page in the light theme

Drawer Gallery page in the dark theme

Drawer Gallery page in the high contrast theme