Skip to content

Dialog

Dialog is a lookless ContentControl whose own template is the backdrop plus panel; the web family’s Root/Trigger/Portal/Backdrop/Popup/Title/Description/Close parts are folded into this one control plus a CloseCommand binding. It renders inside the window’s NaviusOverlayLayer (the WPF analogue of the web’s document.body portal target) and drives modality, Escape and outside-click dismissal, and focus cycling through the shared OverlayStack.

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:dialog="clr-namespace:Navius.Wpf.Primitives.Controls.Dialog;assembly=Navius.Wpf.Primitives"
<Grid>
<!-- app content -->
<navius:NaviusButton Content="Open" Click="OnOpenDialog" />
<!-- one layer per window, stretched over the root -->
<overlay:NaviusOverlayLayer>
<dialog:NaviusDialog x:Name="Confirm"
Title="Confirm change"
Description="This updates the shared settings.">
<StackPanel>
<TextBlock Text="Apply the change?" />
<navius:NaviusButton Content="Close"
Command="overlay:NaviusOverlaySurfaceBase.CloseCommand" />
</StackPanel>
</dialog:NaviusDialog>
</overlay:NaviusOverlayLayer>
</Grid>

A dialog is declared as a direct XAML child of the layer (it is never re-parented at runtime) and stays Collapsed until opened via IsOpen, Open(), or a binding. A window with no NaviusOverlayLayer still parses; opening then logs a Trace.TraceWarning and reverts IsOpen to false.

Property Type Default From Description
IsOpen bool false NaviusOverlaySurfaceBase Open state. Two-way bindable by default. Setting it engages or disengages the overlay; 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 NaviusDialog When true: focus is trapped, the backdrop shows, outside interaction is blocked. When false: no trap, no backdrop, outside content stays interactive.
CloseOnOutsideClick bool true NaviusDialog Whether a press outside the panel dismisses the dialog. Independently overridable, matching the web contract’s optional outside dismissal.
Content object - ContentControl The dialog 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 NaviusDialogClose part.
Event Signature Fires when
Opened EventHandler The overlay has engaged: pushed onto the OverlayStack, enter animation started.
Closing EventHandler<OverlayClosingEventArgs> A close was requested (Escape, outside press, CloseCommand, or IsOpen = false). Cancelable: set Cancel to keep the dialog open; IsOpen then stays true.
Closed EventHandler The exit animation completed and the surface left its layer.
Key Behavior
Escape Closes the dialog. Routed through the window-level OverlayStack PreviewKeyDown hook; fixed on (not configurable), matching the parity contract.
Tab / Shift + Tab Cycles focus among the dialog’s focusable descendants while modal, via KeyboardNavigationMode.Cycle applied by the OverlayStack focus trap.

NaviusDialogAutomationPeer (a FrameworkElementAutomationPeer) reports AutomationControlType.Window and overrides IsDialogCore() to return true, the native mapping for the web’s role="dialog". On engage, Title and Description are applied as AutomationProperties.Name and AutomationProperties.HelpText, which the default peer surfaces without further overrides; the same treatment applies uniformly across Dialog, AlertDialog, and Drawer. Focus moves to the first focusable descendant on open and is restored on close while modal. The unit suite exercises this against real windows, including the M6 regression for a canceled Closing keeping IsOpen, the surface, and the session consistent.

  • One control instead of eight parts: the web’s Root/Trigger/Portal/Backdrop/Popup/ Title/Description/Close anatomy is folded into NaviusDialog plus CloseCommand, following the same shape this codebase uses for NaviusRadioGroup and NaviusCheckboxGroup. There is no discrete Trigger element; any button or binding opens the dialog.
  • NaviusOverlayLayer replaces the web’s CSS-selector Container parameter: a window registers at most one layer, and surfaces are declared as its direct XAML children. System.Windows.Controls.Primitives.Popup hosting was rejected because a Popup renders through its own top-level surface, invisible to the OverlayStack’s window-level key and outside-press routing.
  • Title and Description are string properties feeding UIA name and help text, not mounted sub-elements with generated id wiring (aria-labelledby and aria-describedby have no WPF equivalent).
  • Enter and exit transitions are a fixed 150ms opacity fade via DoubleAnimation (exit completion is what unmounts, so it is never truncated); the web drives these from data-starting-style and data-ending-style CSS hooks. The primitive does not reference Navius.Wpf.Motion.
  • CloseOnEscape is fixed true and not exposed (the web family never overrides it either); CloseOnOutsideClick is exposed as a property.
  • Known residual (M6 audit): a non-modal dialog (Modal="false") does not restore focus to the previously focused element on close, because the OverlayStack only captures a restore target when trapping focus. The web contract restores focus in both paths.

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

Dialog Gallery page in the light theme

Dialog Gallery page in the dark theme

Dialog Gallery page in the high contrast theme