PleasantDialog

April 2, 2026 · View on GitHub

A rich modal dialog with header icon, subheader, command items (radio buttons, checkboxes, command links), progress bar, expandable footer, and full lifecycle events.

Basic usage

object result = await PleasantDialog.Show(window,
    header: "Sync settings",
    body:   "Choose how your settings should be synchronized.");

Parameters

ParameterTypeDescription
headerstringBold title (localization key or raw string)
bodystring?Body text below the header
buttonsIReadOnlyList<PleasantDialogButton>?Bottom buttons — defaults to a single OK
commandsIReadOnlyList<PleasantDialogCommand>?Radio buttons, checkboxes, or command links in the body
iconGeometryKeystring?Resource key of a Geometry shown next to the header
headerBackgroundIBrush?Custom header background
iconForegroundIBrush?Custom icon color
subHeaderstring?Secondary text below the title
extraContentControl?Arbitrary control placed below commands
onContentReadyAction<Control>?Called after extra content is placed, before dialog shows
onDialogReadyAction<PleasantDialog>?Called with the dialog instance before it is shown — use for progress bar updates
footerobject?Footer content
footerExpandableboolWhether the footer is hidden behind a toggle
footerToggleTextstring?Label for the footer toggle button
styleMessageBoxStyleDefault or Danger (red header)

PleasantDialogButton

new PleasantDialogButton
{
    Text         = "Save",
    DialogResult = PleasantDialogResult.OK,
    IsDefault    = true   // gets accent styling and Enter key
}

Predefined buttons: PleasantDialogButton.OK, .Cancel, .Yes, .No, .Close, .Retry

Command items

commands: new PleasantDialogCommand[]
{
    new PleasantDialogRadioButton { Text = "Option A", IsChecked = true },
    new PleasantDialogRadioButton { Text = "Option B" },
    new PleasantDialogCheckBox   { Text = "Remember my choice" },
    new PleasantDialogCommandLink
    {
        Text        = "Advanced setup",
        Description = "Configure additional options manually.",
        ClosesOnInvoked = true,
        DialogResult    = "advanced"
    }
}

Rich dialog example

object result = await PleasantDialog.Show(window,
    header:          "Sync settings",
    body:            "Choose how your settings should be synchronized.",
    iconGeometryKey: "TuneRegular",
    subHeader:       "Affects all connected accounts.",
    commands: new PleasantDialogCommand[]
    {
        new PleasantDialogRadioButton { Text = "Sync automatically",  IsChecked = true },
        new PleasantDialogRadioButton { Text = "Ask before syncing" },
        new PleasantDialogCheckBox   { Text = "Remember my choice" }
    },
    buttons: new[]
    {
        new PleasantDialogButton { Text = "Save",   DialogResult = PleasantDialogResult.OK,     IsDefault = true },
        new PleasantDialogButton { Text = "Cancel", DialogResult = PleasantDialogResult.Cancel }
    },
    footer:           new TextBlock { Text = "Changes take effect after restart." },
    footerExpandable: true,
    footerToggleText: "More details");
object result = await PleasantDialog.Show(window,
    header: "Delete account",
    body:   "This cannot be undone.",
    style:  MessageBoxStyle.Danger,
    commands: new PleasantDialogCommand[]
    {
        new PleasantDialogCommandLink
        {
            Text        = "Delete everything",
            Description = "Removes all files and account data permanently.",
            DialogResult    = PleasantDialogResult.OK,
            ClosesOnInvoked = true
        }
    },
    buttons: new[]
    {
        new PleasantDialogButton { Text = "Cancel", DialogResult = PleasantDialogResult.Cancel, IsDefault = true }
    });

Progress bar

Use onDialogReady to get a reference to the dialog and call SetProgressBarState from a background task:

object result = await PleasantDialog.Show(window,
    header: "Processing",
    body:   "Please wait…",
    buttons: new[] { new PleasantDialogButton { Text = "Cancel", DialogResult = PleasantDialogResult.Cancel } },
    onDialogReady: dialog =>
    {
        dialog.SetProgressBarState(0);
        _ = Task.Run(async () =>
        {
            for (int i = 0; i <= 100; i += 5)
            {
                await Task.Delay(100);
                dialog.SetProgressBarState(i);
            }
            Dispatcher.UIThread.Post(() => _ = dialog.CloseAsync());
        });
    });

Closing event

var d = /* PleasantDialog instance */;
d.Closing += (_, args) =>
{
    if (!CanClose())
        args.Cancel = true;
};