Skip to content

Tooltip

Tooltip is a lookless ContentControl whose Content is the trigger and whose TooltipContent is the bubble. It is built entirely custom on the shared NaviusAnchoredPopup, not on WPF’s native ToolTipService, so it can implement the web contract’s hover-intent delay, cross-tooltip skip-delay grace window, and hoverable-content bridging exactly. App-wide timing defaults live on the static NaviusTooltipService, the WPF analogue of the web’s NaviusTooltipProvider.

xmlns:navius="clr-namespace:Navius.Wpf.Primitives.Controls;assembly=Navius.Wpf.Primitives"
<navius:NaviusTooltip TooltipContent="Saves the current document.">
<navius:NaviusButton Content="Save" />
</navius:NaviusTooltip>

No NaviusOverlayLayer is required: Tooltip hosts its bubble through a native Popup (NaviusAnchoredPopup) and pushes directly onto OverlayStack.GetFor(window). App-wide defaults are set once, anywhere, via the static companion: NaviusTooltipService.DelayDuration = 500;.

Property Type Default From Description
TooltipContent object? null NaviusTooltip Content of the tooltip bubble.
Side PlacementSide Top NaviusTooltip Which side of the trigger the bubble is placed against.
Align PlacementAlign Center NaviusTooltip Cross-axis alignment relative to the trigger.
SideOffset double 6 NaviusTooltip Gap, in DIPs, between the trigger and the bubble along Side.
AlignOffset double 0 NaviusTooltip Offset, in DIPs, along the cross axis.
DelayDuration int? null NaviusTooltip Per-instance hover-intent delay override, in ms. Falls back to NaviusTooltipService.DelayDuration when unset; collapses the web’s DelayDuration/legacy OpenDelay pair into one property.
DisableHoverableContent bool false NaviusTooltip When true, leaving the trigger closes the tooltip even if the pointer moves toward the bubble; skips the hover-bridge grace timer.
IsOpen bool false NaviusTooltip Open state. Two-way bindable by default.
IsInstant bool false NaviusTooltip (read-only) True when the tooltip opened without its hover-intent delay (keyboard focus, or the cross-tooltip skip-delay window). Mirrors the web’s data-instant.
Content object - ContentControl The trigger.
DelayDuration (static) int 700 NaviusTooltipService App-wide default hover-intent delay, in ms, used by every NaviusTooltip instance that leaves its own DelayDuration unset.
SkipDelayDuration (static) int 300 NaviusTooltipService App-wide grace window, in ms, after any tooltip closes during which the next one to open skips its delay entirely.
Method Description
NaviusTooltipService.ShouldSkipDelay() Static: true if a tooltip closed recently enough (within SkipDelayDuration) that the next one should open instantly.
NaviusTooltipService.NotifyClosed() Static: records that a tooltip just closed, (re)starting the skip-delay window. Called by NaviusTooltip itself on close.
NaviusTooltipService.Reset() Static: test/reset hook that clears the skip-delay window.
Key / Event Behavior
Focus (keyboard) Opens instantly, no delay, and marks IsInstant = true. WPF exposes no built-in way to distinguish Tab-driven from mouse-click-driven focus, so both open instantly (a deliberate, documented approximation, not a regression).
Blur Starts the close-grace timer, or closes immediately if DisableHoverableContent.
Escape (trigger focused, tooltip open) Force-closes immediately, bypassing the hoverable grace window.
Space / Enter (trigger focused, tooltip open) Force-closes immediately, so the tooltip doesn’t linger over the activated control.
Pointer enter (trigger) Opens after DelayDuration (falling back to NaviusTooltipService.DelayDuration), or instantly if NaviusTooltipService.ShouldSkipDelay() is true.
Pointer leave (trigger) Starts a 60ms hover-bridge grace timer before closing, to bridge the trigger-to-bubble gap without flicker; closes immediately if DisableHoverableContent.
Pointer down (trigger, while open) Force-closes immediately.
Pointer enter (bubble content) Cancels a pending close-grace timer, unless DisableHoverableContent.
Pointer leave (bubble content) Force-closes immediately, unless DisableHoverableContent.

NaviusTooltip does not override OnCreateAutomationPeer, so it ships no custom automation peer or role="tooltip"-equivalent mapping; the bubble is non-modal and never receives focus (TrapFocus = false, RestoreFocus = false when the session is pushed). The unit suite (TooltipTests.cs) exercises the focus-path open, which is always instant via OnTriggerGotKeyboardFocus and marks IsInstant, the DelayDuration default-value assertion, the Escape and pointer-down force-close paths, the hoverable-content grace window with DisableHoverableContent both on (no synchronous close on MouseLeave) and off (immediate close), and NaviusTooltipService’s skip-delay grace window. There is no test that drives the MouseEnter hover path itself, so the hover-intent open delay (DelayDuration actually elapsing before the tooltip opens on a real pointer hover) has no direct test coverage.

  • Built entirely custom on NaviusAnchoredPopup rather than native ToolTipService, for full control over the hover-intent/skip-delay/hoverable-content timing the web contract specifies.
  • The web’s NaviusTooltipProvider collapses into the static NaviusTooltipService: there is no provider scope, matching WPF’s own ToolTipService convention rather than a cascading-parameter tree. All NaviusTooltip instances in the process share one delay and one skip-delay window.
  • DelayDuration and the web’s legacy OpenDelay alias collapse into one nullable DelayDuration that falls back to the service default.
  • No arrow glyph in the default template: NaviusAnchoredPopup does not currently surface ArrowOffset for this control to consume (owned by another agent/wave).
  • Keyboard-focus detection cannot distinguish Tab-driven from mouse-click-driven focus in WPF, so both open the tooltip instantly; documented as a deliberate honest approximation, not a regression (native ToolTip makes the same non-distinction).
  • No exit animation: IsOpen = false hides the underlying Popup synchronously, keeping the close path deterministic and unit-testable without pumping a dispatcher timer, at the cost of an asymmetric enter/exit (the web drives both from data-starting-style/data-ending-style).
  • No cancelable dismiss callbacks are exposed as public events; OverlaySession.Closing/Closed remain available internally but are not surfaced on NaviusTooltip itself.

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

Tooltip Gallery page in the light theme

Tooltip Gallery page in the dark theme

Tooltip Gallery page in the high contrast theme