Skip to content

Shell | Dialogs

All user-facing dialogs in Shiny Shell go through the IDialogs interface. Inject it into your ViewModels via constructor injection.

public class MyViewModel(IDialogs dialogs)
{
// dialogs is ready to use
}

IDialogs is registered as a singleton by UseShinyShell() and dispatches all calls to the UI thread automatically — safe to call from any thread.

The default IDialogs implementation (ShellDialogs) uses MAUI’s built-in Shell dialogs. You can replace it with your own implementation — for example to plug in a third-party dialog library, a custom bottom-sheet, or a test double — using UseDialogs<TDialog>():

builder
.UseMauiApp<App>()
.UseShinyShell(x => x
.AddGeneratedMaps()
.UseDialogs<MyCustomDialogs>()
);
public class MyCustomDialogs : IDialogs
{
public Task Alert(string? title, string message, string acceptText = "OK") { /* ... */ }
public Task<bool> Confirm(string? title, string message, string acceptText = "Yes", string cancelText = "No") { /* ... */ }
public Task<string?> Prompt(string? title, string message, string acceptText = "OK", string cancelText = "Cancel", string? placeholder = null, string initialValue = "", int maxLength = -1, Keyboard? keyboard = null) { /* ... */ }
public Task<string> ActionSheet(string? title, string? cancel, string? destruction, params string[] buttons) { /* ... */ }
}

The default registration uses TryAddSingleton, so calling UseDialogs<>() always wins — regardless of call order.

Display an informational dialog with a single button.

await dialogs.Alert("Error", "Something went wrong");
// With custom button text
await dialogs.Alert("Success", "Item saved successfully", "Got it");
ParameterTypeDefaultDescription
titlestring?Alert title. Pass null to omit.
messagestringAlert body text (required)
acceptTextstring"OK"Button text

Display a confirmation dialog and return the user’s choice.

bool confirmed = await dialogs.Confirm(
"Delete Item",
"Are you sure you want to delete this?",
"Delete",
"Cancel"
);
if (confirmed)
{
// proceed with deletion
}
ParameterTypeDefaultDescription
titlestring?Dialog title. Pass null to omit.
messagestringDialog body text (required)
acceptTextstring"Yes"Accept button text
cancelTextstring"No"Cancel button text

Display a text input dialog. Returns the entered text, or null if the user cancelled.

// Simple prompt
var name = await dialogs.Prompt("Name", "What is your name?");
if (name != null)
{
// user entered a value
}
// With all options
var pin = await dialogs.Prompt(
"Security",
"Enter your PIN",
acceptText: "Submit",
cancelText: "Cancel",
placeholder: "4-digit PIN",
initialValue: "",
maxLength: 4,
keyboard: Keyboard.Numeric
);
ParameterTypeDefaultDescription
titlestring?Dialog title. Pass null to omit.
messagestringDialog body text (required)
acceptTextstring"OK"Accept button text
cancelTextstring"Cancel"Cancel button text
placeholderstring?nullPlaceholder text shown when the input is empty
initialValuestring""Pre-filled input value
maxLengthint-1Maximum characters allowed (-1 = no limit)
keyboardKeyboard?nullKeyboard type (Keyboard.Numeric, Keyboard.Email, etc.)

Display an action sheet with multiple options. Returns the text of the selected button.

var action = await dialogs.ActionSheet(
"Photo Options",
"Cancel",
"Delete Photo",
"Take Photo", "Choose from Library", "Share"
);
switch (action)
{
case "Take Photo":
// open camera
break;
case "Choose from Library":
// open gallery
break;
case "Share":
// share photo
break;
case "Delete Photo":
// destructive action
break;
}
ParameterTypeDescription
titlestring?Sheet title. Pass null to omit.
cancelstring?Cancel button text. Pass null to omit.
destructionstring?Destructive action text (shown in red on some platforms). Pass null to omit.
buttonsstring[]Action button labels

Use IDialogs with INavigationConfirmation to guard unsaved changes:

public class EditViewModel(INavigator navigator, IDialogs dialogs) :
INavigationConfirmation
{
public bool HasUnsavedChanges { get; set; }
public async Task<bool> CanNavigate()
{
if (!HasUnsavedChanges)
return true;
return await dialogs.Confirm(
"Unsaved Changes",
"You have unsaved changes. Discard them?"
);
}
}
public class ItemViewModel(INavigator navigator, IDialogs dialogs)
{
async Task DeleteItem()
{
if (await dialogs.Confirm("Delete", "This action cannot be undone."))
{
await itemService.Delete(ItemId);
await navigator.GoBack(("Deleted", true));
}
}
}
public class ListViewModel(IDialogs dialogs)
{
async Task RenameItem(Item item)
{
var newName = await dialogs.Prompt(
"Rename",
"Enter a new name",
placeholder: "New name",
initialValue: item.Name,
maxLength: 100
);
if (newName != null)
item.Name = newName;
}
}