A fully-featured tooltip and popover framework with 20 semantic types, 7 layout variants, 8 animations, 7 built-in validators, guided-tour support, pinnable popovers, and a unified painter pipeline — all integrated with the Beep theme system.
// Attach to a control
ToolTipBuilder.Create()
.WithTitle("Save File")
.WithText("Saves the current document to disk.")
.WithTheme(ToolTipType.Info)
.WithPlacement(ToolTipPlacement.Top)
.WithAnimation(ToolTipAnimation.Slide)
.WithArrow(true)
.WithShadow(true)
.Closable(false)
.AttachTo(btnSave);
// Show at a point
await ToolTipBuilder.Create()
.WithText("Processing…")
.WithTheme(ToolTipType.Notification)
.WithDuration(2000)
.ShowAsync(cursorPosition);
All builder methods return this for chaining. Available builder methods:
Method
Description
WithText(text)
Set body text.
WithTitle(title)
Set title.
WithTheme(type)
Set ToolTipType semantic theme.
WithCustomColors(back, fore, border?)
Override colors (sets Type = Custom).
WithPlacement(placement)
Set preferred position.
WithAnimation(animation)
Set show/hide animation.
WithDuration(ms)
Set auto-dismiss duration.
WithDelay(showMs, hideMs?)
Set show and hide delays.
WithMaxSize(size)
Constrain tooltip dimensions.
WithFont(font)
Override font.
WithIcon(image)
Set icon image object.
WithIconPath(path)
Set icon from file path.
FollowCursor(bool)
Enable cursor-tracking.
WithArrow(bool)
Show/hide arrow.
WithShadow(bool)
Show/hide shadow.
Closable(bool)
Show/hide × button.
OnShow(action)
Show callback.
OnClose(action)
Close callback.
Build()
Return the ToolTipConfig.
ShowAsync(point)
Build and show at position.
AttachTo(control)
Build and attach to control.
BeepPopover — Persistent Popup
A persistent window that stays open until explicitly dismissed. Extends CustomToolTip. Configured via PopoverConfig (extends ToolTipConfig).
PopoverConfig Properties
Property
Type
Default
Description
PrimaryButtonText
string
—
Label for primary/confirm button. Omit to hide.
SecondaryButtonText
string
—
Label for secondary/cancel button. Omit to hide.
PrimaryButtonType
ToolTipType
Primary
Color theme of the primary button (e.g. Error for destructive actions).
OnPrimaryClick
Action<string>
—
Fired when primary button clicked. Argument is the Key.
OnSecondaryClick
Action<string>
—
Fired when secondary button clicked or popover cancelled.
DismissOnClickOutside
bool
true
Close when the user clicks outside the popover.
DismissOnEscape
bool
true
Close when the user presses Escape.
MaxPopoverWidth
int
360
Maximum width of the popover in pixels.
BeepPopover Public API
Method
Description
ApplyPopoverConfig(cfg)
Apply a PopoverConfig and mount action buttons.
ConfirmAndClose()
Invoke OnPrimaryClick callback and close.
DismissAsCancel()
Invoke OnSecondaryClick callback and close.
// Confirmation popover
await ToolTipManager.Instance.ShowPopoverAsync(btnDelete, new PopoverConfig
{
Title = "Delete item?",
Text = "This action cannot be undone.",
Type = ToolTipType.Error,
PrimaryButtonText = "Delete",
PrimaryButtonType = ToolTipType.Error,
SecondaryButtonText = "Cancel",
TriggerMode = ToolTipTriggerMode.Click,
Placement = ToolTipPlacement.Top,
OnPrimaryClick = key => DeleteItem(),
OnSecondaryClick = key => { /* do nothing */ }
});
BeepPinnedTooltip — Draggable Pinned Window
Extends BeepPopover. When pinned, the window stays on screen and can be dragged anywhere. A pin button appears in the top-right corner.
Member
Description
IsPinned
bool property — get/set pin state. When true, auto-dismiss is disabled.
IsPinnedChanged
Event — fires when pin state toggles.
Pin()
Pin the tooltip at its current screen position.
Unpin()
Unpin — tooltip will auto-dismiss on next hide trigger.
ApplyPinnedConfig(config, screenLocation)
Apply config with pre-set IsPinned and position.
// Enable pinnable tooltip via ToolTipConfig
btnHelp.SetTooltip(new ToolTipConfig
{
Title = "Detailed Help",
Text = "This panel controls the …",
Pinnable = true,
Closable = true,
Duration = 0, // no auto-dismiss
Placement = ToolTipPlacement.Right
});
Guided Tour — BeepTourManager & BeepTourBuilder
A lightweight guided-tour / onboarding engine built on the tooltip stack. All tooltips are ToolTipLayoutVariant.Tour with step indicators and Prev/Next navigation.
BeepTourStep Properties
Property
Type
Description
TargetControl
Control
The control to highlight and anchor this step's tooltip to.
Title
string
Primary heading.
Body
string
Descriptive text.
ImagePath
string
Optional image shown above the text.
Placement
ToolTipPlacement
Where to anchor the tooltip relative to TargetControl.
OnEnter
Action
Called just before this step is shown (e.g. scroll into view).
OnLeave
Action
Called just after this step is dismissed.
BeepTourManager API
Member
Description
BeepTourManager.Instance
Singleton instance.
IsRunning
True while a tour is active.
CurrentStep
1-based current step number.
TotalSteps
Total step count.
CreateTour()
Return a new BeepTourBuilder bound to this manager.
LoadTour(steps)
Load a pre-built step list.
StartAsync() / Start()
Begin the tour from step 1.
NextAsync() / Next()
Advance to next step; completes tour if on last step.
PreviousAsync() / Previous()
Go back to previous step.
Skip()
Cancel tour without completing.
TourStarted
Event — fired when tour begins.
StepChanged
EventHandler<(int current, int total)> — fired on each step transition.
TourCompleted
Event — fired when all steps completed.
TourSkipped
Event — fired when Skip() called.
BeepTourBuilder Fluent API
Method
Description
AddStep(control, title, body, placement)
Add a basic step.
AddStep(BeepTourStep)
Add a fully pre-configured step.
WithCallbacks(onEnter?, onLeave?)
Attach lifecycle callbacks to the most recently added step.
WithImage(imagePath)
Attach an image to the most recently added step.
Build()
Validate, load into BeepTourManager, and return the manager for chaining.
// ── Fluent builder ──────────────────────────────────────────────
BeepTourManager.Instance
.CreateTour()
.AddStep(btnNew, "Create New", "Click here to create a new record")
.AddStep(btnSave, "Save", "Saves all pending changes")
.WithCallbacks(onEnter: () => btnSave.Focus())
.AddStep(tbxSearch, "Search", "Type to filter the grid rows")
.WithImage(@"Assets\tour-search.png")
.AddStep(btnExport, "Export", "Export results to Excel or CSV",
ToolTipPlacement.Left)
.Build()
.Start();
// ── Events ──────────────────────────────────────────────────────
BeepTourManager.Instance.TourCompleted += (s, e) =>
Properties.Settings.Default.TourSeen = true;
BeepTourManager.Instance.StepChanged += (s, args) =>
lblStep.Text = $"Step {args.current} of {args.total}";
Painter Pipeline
All rendering is delegated through IToolTipPainter. ToolTipPainterFactory selects the correct painter based on ToolTipLayoutVariant.
Painter Class
Used For
BeepStyledToolTipPainter
All standard variants: Simple, Rich, Card, Shortcut, and fallback.
// Implement the interface
public class MyCustomPainter : ToolTipPainterBase
{
public override void PaintContent(Graphics g, Rectangle bounds,
ToolTipConfig config, IBeepTheme theme)
{
// custom rendering
}
}
// Use it via ToolTipLayoutVariant.Custom requires overriding GetPainter,
// or set directly on the CustomToolTip instance before Show().
Code Examples
Simple tooltip on any control
btnSave.SetTooltip("Save your changes (Ctrl+S)");
Rich titled tooltip
btnSave.SetRichTooltip(
text: "Saves all unsaved changes to the current document.",
title: "Save File",
type: ToolTipType.Info
);
Shortcut badge tooltip
btnSave.SetTooltip(new ToolTipConfig
{
Title = "Save File",
Text = "Save the current document.",
LayoutVariant = ToolTipLayoutVariant.Shortcut,
Shortcuts = new List<ShortcutKeyItem>
{
new ShortcutKeyItem { Modifiers = Keys.Control, Key = Keys.S }
}
});
// After form validation
await txtEmail.ShowErrorAsync("Please enter a valid email address.");
// After a successful save
await btnSave.ShowSuccessAsync("Saved!", duration: 1500);
Glass-style tooltip
statusIcon.SetTooltip(new ToolTipConfig
{
Title = "System Status",
Text = "All services running normally.",
Type = ToolTipType.Success,
LayoutVariant = ToolTipLayoutVariant.Glass,
Animation = ToolTipAnimation.Zoom,
Placement = ToolTipPlacement.BottomStart
});
Programmatic tooltip at cursor
private async void OnGridCellHover(object sender, CellHoverEventArgs e)
{
var pos = grid.PointToScreen(e.CellBounds.Location);
_hoverKey = await ToolTipManager.Instance.ShowTooltipAsync(new ToolTipConfig
{
Title = e.ColumnHeader,
Text = e.CellValue?.ToString() ?? "(empty)",
Type = ToolTipType.Default,
Duration = 0, // keep until cell leave
Placement = ToolTipPlacement.Top,
Position = pos
});
}
private async void OnGridCellLeave(object sender, EventArgs e)
{
if (_hoverKey != null)
{
await ToolTipManager.Instance.HideTooltipAsync(_hoverKey);
_hoverKey = null;
}
}
Follow-cursor tooltip
ToolTipBuilder.Create()
.WithText("Drag to resize")
.WithTheme(ToolTipType.Hint)
.WithAnimation(ToolTipAnimation.Fade)
.FollowCursor(true)
.WithArrow(false)
.AttachTo(resizeHandle);
Best Practices
Use extension methods for simple cases.control.SetTooltip("text") is always sufficient for plain hover tooltips — no manual ToolTipManager calls needed.
Use ToolTipBuilder for one-off styled tooltips. The fluent API avoids repeatedly constructing ToolTipConfig objects.
Use ToolTipType semantically. Choose Error for errors, Warning for cautions — the type drives accessible color selection including WCAG contrast enforcement (MinContrastRatio).
Set Duration = 0 for popovers and pinnable tooltips. Never let an interactive or pinned tooltip auto-dismiss.
Use PersistOnHover = true (default) for all rich tooltips. This satisfies WCAG 1.4.13 — users must be able to move the mouse over the tooltip.
Wire TriggerMode = Click for destructive confirmation popovers. Hover-triggered confirmation dialogs are confusing; click-triggered ones are expected.
Prefer LoadPreviewAsync for heavy images in Preview tooltips — the skeleton placeholder displays immediately while the image loads.
Call HideAllTooltipsAsync() when opening a modal dialog to prevent orphaned tooltips underneath.
Disable animations during tests:ToolTipManager.Instance.EnableAnimations = false;
Always set TriggerMode = Manual when managing lifecycle yourself (grid cells, map pins, etc.) so the manager does not interfere with your MouseEnter/Leave logic.
Tour: call Properties.Settings.Default.TourSeen after TourCompleted to avoid showing the tour to returning users.