A comprehensive, production-ready dialog library for .NET MAUI applications with built-in theming, localization, hierarchical menus, and extensive customization options.
- Features
- Installation
- Quick Start
- Dialog Types
- Notifications
- Theming
- Localization
- Advanced Features
- API Reference | Full API Documentation
- Requirements
- Migration Guide
- Troubleshooting
- Contributing
- License
- 12 Dialog Types: Alert, Confirm, Prompt, Editor, Loading, Action List, Color Picker, Date Picker, Time Picker, DateTime Picker, Toast, and Snackbar
- Date/Time Pickers: Native date and time selection with "Now" quick-select buttons (v1.5.0+)
- Design System Presets: Material Design 3, Fluent Design, and Cupertino themes (v1.5.0+)
- Toast & Snackbar: Lightweight notifications with optional actions and stacking (v1.4.0+)
- ActionItem Callbacks: Define Action/AsyncAction directly on items for automatic invocation (v1.4.1+)
- Hierarchical Menus: Multi-level action list navigation with automatic back navigation (v1.3.0+)
- Adaptive Theming: Automatic dark/light theme detection with full customization
- Internationalization: Built-in support for English, Spanish, French, and German
- HTML Description Support: Rich text formatting in dialog descriptions
- Cross-Platform: iOS 11.0+, Android API 21+, Windows 10 (17763+), macOS 10.15+
- Type-Safe APIs: Strongly typed with comprehensive IntelliSense support
- Async/Await Pattern: Modern asynchronous dialog handling
- Memory Efficient: Intelligent image caching and resource management
- Extensible Architecture: Easy to create custom dialogs via
BaseDialog - Thread-Safe: Singleton service pattern with proper synchronization (v1.5.0+)
- Production-Ready: IDisposable pattern, proper exception handling, and logging support (v1.5.0+)
- Symbol Package Support: Full debugging support with
.snupkgpackages
Install via NuGet Package Manager:
dotnet add package MarketAlly.Dialogs.MauiOr via Package Manager Console:
Install-Package MarketAlly.Dialogs.MauiOr search for MarketAlly.Dialogs.Maui in the NuGet Package Manager UI.
using MarketAlly.Dialogs.Maui;
using Mopups.Hosting;
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureMopups() // Required for popup functionality
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
return builder.Build();
}// In App.xaml.cs
protected override void OnStart()
{
// Optional: Initialize with default settings
DialogService.Instance.Initialize();
// Optional: Set overlay preferences
DialogService.Instance.SetOverlayEnabled(true);
DialogService.Instance.SetOverlayColor(Color.FromRgba("#80000000"));
}using MarketAlly.Dialogs.Maui.Dialogs;
// Simple alert
await AlertDialog.ShowAsync("Welcome!", "Thanks for using our dialogs", DialogType.Success);Display informational messages with customizable icons and styling.
// Simple alert
await AlertDialog.ShowAsync("Operation Complete", DialogType.Success);
// With description
await AlertDialog.ShowAsync(
"Network Error",
"Unable to connect to server. Please check your connection.",
DialogType.Error
);
// Custom button and styling
var dialog = new AlertDialog(
"Important Notice",
"Please read the terms carefully",
"I Understand",
DialogType.Warning);
dialog.DescriptionPadding = new Thickness(20);
await dialog.ShowAsync();Get user confirmation with customizable buttons.
// Simple yes/no
bool confirmed = await ConfirmDialog.ShowAsync(
"Delete Item",
"This action cannot be undone. Continue?"
);
// Custom buttons
var dialog = new ConfirmDialog(
"Save Changes",
"You have unsaved changes. What would you like to do?",
"Save & Exit",
"Discard",
DialogType.Warning);
bool result = await dialog.ShowAsync();Collect single-line text input with validation support and password visibility toggle.
// Basic text input
string? name = await PromptDialog.ShowAsync(
"Enter Name",
"Your full name"
);
// Password input with visibility toggle
var dialog = new PromptDialog(
"Enter Password",
"Password",
"Login",
"Cancel",
DialogType.None,
Keyboard.Text,
isPassword: true);
string? password = await dialog.ShowAsync();
// Email with specific keyboard
string? email = await PromptDialog.ShowAsync(
"Email Address",
"user@example.com",
Keyboard.Email,
DialogType.Info
);Collect multi-line text with configurable constraints, spell check, and text prediction.
// Basic multi-line input
string? notes = await EditorDialog.ShowAsync(
"Add Notes",
"Enter your notes here",
"Type your notes...",
DialogType.None
);
// Advanced configuration
var dialog = new EditorDialog(
"Feedback",
"Help us improve",
"Your feedback...",
"Submit",
"Cancel",
DialogType.Help,
Keyboard.Text,
minLines: 3,
maxLines: 10);
dialog.IsSpellCheckEnabled = true;
dialog.IsTextPredictionEnabled = true;
string? feedback = await dialog.ShowAsync();Show progress indicators with optional cancellation support.
// Simple loading
await LoadingDialog.ShowAsync("Processing...", async () =>
{
await ProcessDataAsync();
});
// With cancellation
bool wasCanceled = await LoadingDialog.ShowCancelableAsync(
"Downloading... Click Cancel to stop",
async () =>
{
for (int i = 0; i < 100; i++)
{
await Task.Delay(100);
// Check cancellation token
}
}
);
// Manual control
var loading = new LoadingDialog("Uploading...");
await MopupService.Instance.PushAsync(loading);
// ... do work
await MopupService.Instance.RemovePageAsync(loading);Present a list of actions with optional icons, descriptions, and hierarchical sub-menus. Supports multi-line descriptions with customizable truncation and wrapping behavior.
// Basic action list
var actions = new List<ActionItem>
{
new ActionItem("Share", "Share with others", 0),
new ActionItem("Edit", "Modify the item", 1),
new ActionItem("Delete", "Remove permanently", 2),
new ActionItem("Archive", "Move to archive", 3)
};
var dialog = new ActionListDialog(
"Choose Action",
actions,
"Cancel"
);
int result = await dialog.ShowAsync();
if (result >= 0)
{
// Action selected (0-3)
var selectedAction = actions[result];
await HandleAction(selectedAction);
}
// With icons
var actionsWithIcons = new List<ActionItem>
{
new ActionItem("Share", "Share with others", 0,
"share_icon_dark.png", "share_icon_light.png"),
new ActionItem("Edit", "Modify the item", 1,
"edit_icon_dark.png", "edit_icon_light.png")
};
// Hierarchical menus (NEW in v1.3.0)
var menuWithSubItems = new List<ActionItem>
{
new ActionItem("File", "File operations", 0)
{
SubItems = new List<ActionItem>
{
new ActionItem("New", "Create new file", 10),
new ActionItem("Open", "Open existing file", 11),
new ActionItem("Save", "Save current file", 12)
}
},
new ActionItem("Edit", "Edit operations", 1)
{
SubItems = new List<ActionItem>
{
new ActionItem("Cut", "Cut selection", 20),
new ActionItem("Copy", "Copy selection", 21),
new ActionItem("Paste", "Paste from clipboard", 22)
}
},
new ActionItem("Settings", "Open settings", 2)
};
var hierarchicalDialog = new ActionListDialog(
"Main Menu",
menuWithSubItems,
"Cancel"
);
// User can navigate through sub-menus
// Back button automatically handles navigation
int selected = await hierarchicalDialog.ShowAsync();
// Multi-line descriptions
var premiumActions = new List<ActionItem>
{
new ActionItem("Cloud Sync",
"Automatically sync your files across all devices in real-time. Changes are instantly reflected everywhere you work.",
0, "sync_icon_dark.png", "sync_icon_light.png"),
new ActionItem("Team Collaboration",
"Invite team members to collaborate on projects. Share workspaces, assign tasks, and track progress together.",
1, "team_icon_dark.png", "team_icon_light.png")
};
var multiLineDialog = new ActionListDialog(
"Premium Features",
premiumActions,
"Cancel",
customHeight: null,
descriptionMaxLines: 2,
descriptionLineBreakMode: LineBreakMode.TailTruncation
);
int selectedFeature = await multiLineDialog.ShowAsync();
// Action callbacks (NEW in v1.4.1)
// Define actions directly on items - no switch statement needed!
var actionsWithCallbacks = new List<ActionItem>
{
// Synchronous action
new ActionItem("Share", () => ShareItem(), "Share with others"),
// Async action
new ActionItem("Save", async () => await SaveItemAsync(), "Save to cloud"),
// With icons
new ActionItem("Delete", async () =>
{
var confirmed = await ConfirmDialog.ShowAsync("Delete?", "Are you sure?");
if (confirmed) await DeleteItemAsync();
}, "Remove permanently", "delete_dark.png", "delete_light.png")
};
// Use ShowWithActionsAsync for cleaner code
bool wasSelected = await ActionListDialog.ShowWithActionsAsync(
"Choose Action",
actionsWithCallbacks,
"Cancel"
);
// Actions are automatically invoked when items are selected!Advanced color selection with RGB sliders, hex input, alpha channel support, and preset colors.
// Basic color picker
var dialog = new ColorPickerDialog(
"Choose Theme Color",
"Select your preferred color",
Colors.Blue,
"Select",
"Cancel"
);
Color? selectedColor = await dialog.ShowAsync();
if (selectedColor != null)
{
string hexColor = dialog.GetHexColor();
// Apply the color
}
// With alpha channel
var dialogWithAlpha = new ColorPickerDialog(
"Background Color",
"Choose color with transparency",
Colors.Red.WithAlpha(0.5f),
"Apply",
"Cancel",
showAlpha: true,
showPresets: true
);
// Without preset colors
var customDialog = new ColorPickerDialog(
"Custom Color",
null,
Colors.Green,
"OK",
"Cancel",
showAlpha: false,
showPresets: false
);Select a date with optional constraints and quick "Today" button.
// Basic date picker
DateTime? selectedDate = await DatePickerDialog.ShowAsync(
"Select Date",
"Choose a date for the appointment");
if (selectedDate.HasValue)
{
Console.WriteLine($"Selected: {selectedDate.Value:d}");
}
// With date constraints
DateTime? date = await DatePickerDialog.ShowAsync(
"Select Date",
"Choose a date within the next 30 days",
initialDate: DateTime.Today,
minDate: DateTime.Today,
maxDate: DateTime.Today.AddDays(30));
// Full customization
DateTime? date = await DatePickerDialog.ShowAsync(
"Delivery Date",
"When should we deliver?",
initialDate: DateTime.Today.AddDays(1),
minDate: DateTime.Today,
maxDate: DateTime.Today.AddMonths(3),
okText: "Confirm",
cancelText: "Back",
showTodayButton: true,
showClearButton: false,
dialogType: DialogType.Info);Select a time with optional "Now" quick-select button.
// Basic time picker
TimeSpan? selectedTime = await TimePickerDialog.ShowAsync(
"Select Time",
"Choose a reminder time");
if (selectedTime.HasValue)
{
Console.WriteLine($"Selected: {selectedTime.Value:hh\\:mm}");
}
// With initial time and customization
TimeSpan? time = await TimePickerDialog.ShowAsync(
"Meeting Time",
"When should the meeting start?",
initialTime: new TimeSpan(9, 0, 0), // 9:00 AM
okText: "Set",
cancelText: "Cancel",
showNowButton: true,
showClearButton: false,
dialogType: DialogType.None);Combined date and time selection with responsive layout (horizontal on mobile, vertical on desktop).
// Basic date/time picker
DateTime? selectedDateTime = await DateTimePickerDialog.ShowAsync(
"Select Date & Time",
"Choose when to schedule the event");
if (selectedDateTime.HasValue)
{
Console.WriteLine($"Selected: {selectedDateTime.Value:g}");
}
// With constraints
DateTime? dateTime = await DateTimePickerDialog.ShowAsync(
"Appointment",
"Select appointment date and time",
initialDateTime: DateTime.Now.AddHours(1),
minDate: DateTime.Today,
maxDate: DateTime.Today.AddMonths(6));
// Full customization
DateTime? dateTime = await DateTimePickerDialog.ShowAsync(
"Schedule Meeting",
"Choose date and time",
initialDateTime: DateTime.Now,
minDate: DateTime.Today,
maxDate: DateTime.Today.AddYears(1),
okText: "Schedule",
cancelText: "Cancel",
showNowButton: true,
showClearButton: true,
dialogType: DialogType.Info);Date/Time Picker Features:
- Today/Now Buttons: Quick-select buttons for current date/time
- Clear Button: Optional button to clear the selection
- Date Constraints: Min/max date validation
- Responsive Layout: DateTime picker uses horizontal layout on mobile (Android/iOS) and vertical on desktop (Windows/macOS)
- Theme Integration: Fully themed with all design system presets
Lightweight, non-interactive "fire-and-forget" notifications for quick status updates.
// Simple toast
await Toast.ShowAsync("Message sent");
// With icon
await Toast.ShowAsync("Operation complete!", DialogType.Success);
// With icon and duration
await Toast.ShowAsync("Something went wrong", DialogType.Error, ToastDuration.Long);
// Vertical position (top or bottom)
await Toast.ShowAsync(
"This appears at the top",
DialogType.Info,
ToastDuration.Short,
ToastPosition.Top
);
// Full position control (vertical + horizontal)
await Toast.ShowAsync(
"Bottom right corner!",
DialogType.Success,
ToastDuration.Short,
ToastPosition.Bottom,
ToastHorizontalPosition.Right
);
// All corner positions
await Toast.ShowAsync("Top Left", DialogType.Info, ToastDuration.Short, ToastPosition.Top, ToastHorizontalPosition.Left);
await Toast.ShowAsync("Top Right", DialogType.Warning, ToastDuration.Short, ToastPosition.Top, ToastHorizontalPosition.Right);
await Toast.ShowAsync("Bottom Left", DialogType.Success, ToastDuration.Short, ToastPosition.Bottom, ToastHorizontalPosition.Left);
await Toast.ShowAsync("Bottom Right", DialogType.Error, ToastDuration.Short, ToastPosition.Bottom, ToastHorizontalPosition.Right);
// Custom duration in milliseconds
await Toast.ShowAsync("Custom timing", DialogType.None, 5000, ToastPosition.Bottom);
// Dismiss all toasts
await Toast.DismissAllAsync();Toast Features:
- Vertical Position: Top or Bottom of screen (default: Bottom)
- Horizontal Position: Left, Center, or Right (default: Center)
- Duration: Short (2s) or Long (3.5s), or custom milliseconds
- Icons: Supports all DialogType icons
- Stacking: Multiple toasts can stack, replace, or queue
- Non-blocking: User can continue interacting with the app
Stacking Behavior Configuration:
// Stack multiple toasts (default)
Toast.DefaultStackBehavior = ToastStackBehavior.Stack;
Toast.MaxVisibleToasts = 3; // Maximum visible at once
// Replace existing toasts
Toast.DefaultStackBehavior = ToastStackBehavior.Replace;
// Queue toasts (show one at a time)
Toast.DefaultStackBehavior = ToastStackBehavior.Queue;Actionable notifications with optional buttons for quick user responses.
// Simple snackbar
var result = await Snackbar.ShowAsync("File saved to documents");
// With action button
var result = await Snackbar.ShowAsync("Item deleted", "UNDO");
if (result == SnackbarResult.ActionClicked)
{
// User clicked UNDO
RestoreItem();
}
// With action callback
var result = await Snackbar.ShowAsync(
"Message archived",
"UNDO",
() => UnarchiveMessage() // Called when UNDO is clicked
);
// Full customization
var result = await Snackbar.ShowAsync(
message: "Connection lost",
actionText: "RETRY",
actionCallback: () => Reconnect(),
iconType: DialogType.Error,
duration: SnackbarDuration.Long,
position: ToastPosition.Bottom
);
// Indefinite snackbar (stays until user interaction)
var result = await Snackbar.ShowAsync(
"No internet connection",
"RETRY",
null,
DialogType.Warning,
SnackbarDuration.Indefinite,
ToastPosition.Bottom
);
// Dismiss all snackbars
await Snackbar.DismissAllAsync();Snackbar Features:
- Action Button: Optional button with callback (UNDO, RETRY, VIEW, etc.)
- Position: Top or Bottom of screen (default: Bottom)
- Duration: Short (4s), Long (7s), or Indefinite
- Icons: Supports all DialogType icons
- Swipe to Dismiss: Users can swipe to dismiss
- Stacking: Multiple snackbars can stack, replace, or queue
- Result Tracking: Returns
SnackbarResult(ActionClicked, Dismissed, TimedOut)
Stacking Behavior Configuration:
// Stack multiple snackbars (default)
Snackbar.DefaultStackBehavior = SnackbarStackBehavior.Stack;
Snackbar.MaxVisibleSnackbars = 3;
// Replace existing snackbars
Snackbar.DefaultStackBehavior = SnackbarStackBehavior.Replace;
// Queue snackbars
Snackbar.DefaultStackBehavior = SnackbarStackBehavior.Queue;SnackbarResult Enumeration:
public enum SnackbarResult
{
Dismissed, // User dismissed (swipe, tap outside, back button)
ActionClicked, // User clicked the action button
TimedOut // Snackbar auto-dismissed after duration
}| Use Case | Recommended |
|---|---|
| "Saved" / "Copied" / "Sent" | Toast |
| "Item deleted" with UNDO | Snackbar |
| "Connection lost" with RETRY | Snackbar |
| "Settings updated" | Toast |
| "File uploaded" with VIEW | Snackbar |
| Quick status confirmation | Toast |
| Actions that might need reversal | Snackbar |
The library provides comprehensive theming support with automatic dark/light mode detection.
Apply popular design system themes with a single line of code:
// Material Design 3
DialogService.Instance.CurrentThemeOverride = DialogTheme.Material(); // Light
DialogService.Instance.CurrentThemeOverride = DialogTheme.MaterialDark(); // Dark
DialogService.Instance.CurrentThemeOverride = DialogTheme.Material(isDark: true); // Auto
// Microsoft Fluent Design
DialogService.Instance.CurrentThemeOverride = DialogTheme.Fluent(); // Light
DialogService.Instance.CurrentThemeOverride = DialogTheme.FluentDark(); // Dark
DialogService.Instance.CurrentThemeOverride = DialogTheme.Fluent(isDark: true); // Auto
// Apple Cupertino (iOS/macOS)
DialogService.Instance.CurrentThemeOverride = DialogTheme.Cupertino(); // Light
DialogService.Instance.CurrentThemeOverride = DialogTheme.CupertinoDark(); // Dark
DialogService.Instance.CurrentThemeOverride = DialogTheme.Cupertino(isDark: true); // Auto
// Using the presets class directly
DialogThemePresets.ApplyPreset(DesignSystem.Material, isDark: false);
// Get theme pair for manual switching
var (light, dark) = DialogThemePresets.GetThemePair(DesignSystem.Fluent);Design System Characteristics:
| Property | Material 3 | Fluent | Cupertino |
|---|---|---|---|
| Corner Radius | 28px (dialog), 20px (button) | 8px (dialog), 4px (button) | 14px (dialog), 10px (button) |
| Title Font | 24px Bold | 20px Regular | 17px Bold |
| Animation | 200ms | 167ms | 250ms |
| Shadow | Yes | Yes | No |
| Dialog Width | 312px | 340px | 270px |
var customLightTheme = new DialogTheme
{
// Colors
BackgroundColor = Color.FromRgba("#FFFFFF"),
OverlayColor = Color.FromRgba("#80000000"),
BorderColor = Color.FromRgba("#E0E0E0"),
ShowOverlay = true,
// Text Colors
TitleTextColor = Color.FromRgba("#212121"),
DescriptionTextColor = Color.FromRgba("#757575"),
// Button Colors
ButtonBackgroundColor = Color.FromRgba("#2196F3"),
ButtonTextColor = Color.FromRgba("#FFFFFF"),
SecondaryButtonBackgroundColor = Color.FromRgba("#F5F5F5"),
SecondaryButtonTextColor = Color.FromRgba("#212121"),
// Typography
TitleFontSize = 18,
TitleFontAttributes = FontAttributes.Bold,
TitleMaxLines = 2,
TitleLineBreakMode = LineBreakMode.TailTruncation,
DescriptionFontSize = 14,
DescriptionTextType = TextType.Text, // or TextType.Html for HTML support
ButtonFontSize = 14,
// Layout
DialogWidth = 300,
DialogHeight = 250,
DialogCornerRadius = 12,
DialogPadding = 20,
ButtonHeight = 44,
// Effects
HasShadow = true,
AnimationDuration = 250,
EnableAnimation = true
};
DialogService.Instance.Initialize(customLightTheme, customDarkTheme);// Force dark theme
DialogService.Instance.CurrentThemeOverride = DialogService.Instance.DarkTheme;
// Force light theme
DialogService.Instance.CurrentThemeOverride = DialogService.Instance.LightTheme;
// Return to system theme
DialogService.Instance.CurrentThemeOverride = null;
// Disable background overlay
DialogService.Instance.SetOverlayEnabled(false);
// Custom overlay color
DialogService.Instance.SetOverlayColor(Color.FromRgba("#CC000000"));Control how dialog titles are displayed across all dialog types:
var theme = new DialogTheme
{
TitleMaxLines = 2, // Maximum lines for title (default: 2)
TitleLineBreakMode = LineBreakMode.TailTruncation, // How to truncate/wrap title text
// ... other properties
};
// Available LineBreakMode options:
// - TailTruncation: "This is a very long..." (default) ✅ Works on all platforms
// - HeadTruncation: "...very long title" ⚠️ May not work on Windows
// - MiddleTruncation: "This is...title" ⚠️ May not work on Windows
// - WordWrap: Wraps at word boundaries ✅ Works on all platforms
// - CharacterWrap: Wraps at any character ✅ Works on all platforms
// - NoWrap: No wrapping, may overflow ✅ Works on all platforms
DialogService.Instance.Initialize(theme);Benefits:
- Prevents layout issues with very long titles
- Maintains consistent dialog heights
- Works across all dialog types (Alert, Confirm, Prompt, etc.)
- Customizable per theme (light/dark can have different settings)
Platform Notes:
HeadTruncationandMiddleTruncationmay not render correctly on Windows due to MAUI framework limitations- For cross-platform compatibility, use
TailTruncation(default) orWordWrap
Enable HTML formatting in dialog descriptions for rich text display:
var theme = new DialogTheme
{
DescriptionTextType = TextType.Html, // Enable HTML formatting (default: Text)
// ... other properties
};
DialogService.Instance.Initialize(theme);
// Now you can use HTML in descriptions
await AlertDialog.ShowAsync(
"Welcome!",
"This is <b>bold</b> and this is <i>italic</i>.<br/>New line here!",
DialogType.Info
);Available TextType options:
TextType.Text- Plain text (default, no HTML parsing)TextType.Html- Renders basic HTML tags like<b>,<i>,<u>,<br/>, etc.
Supported HTML tags:
<b>,<strong>- Bold text<i>,<em>- Italic text<u>- Underlined text<br/>- Line breaks- Basic text formatting
Use cases:
- Formatted error messages with bold keywords
- Multi-line descriptions with proper line breaks
- Emphasized text within descriptions
- Rich informational dialogs
The library includes a complete localization framework with built-in translations and extensibility.
- English (default)
- Spanish (es)
- French (fr)
- German (de)
public class JapaneseLocalization : IDialogLocalization
{
public string OkButtonText => "OK";
public string CancelButtonText => "キャンセル";
public string YesButtonText => "はい";
public string NoButtonText => "いいえ";
public string LoadingText => "読み込み中...";
public string SelectPlaceholder => "選択してください";
public string HexLabel => "16進数:";
public string RedLabel => "赤";
public string GreenLabel => "緑";
public string BlueLabel => "青";
public string AlphaLabel => "透明度";
public string PresetColorsLabel => "プリセット色";
public string ItemsScrollIndicator => "{0} 項目 (スクロールで表示)";
public string GetString(string key) => key;
public string GetString(string key, params object[] args)
=> string.Format(GetString(key), args);
}
// Apply the localization
DialogService.Instance.SetLocalization(new JapaneseLocalization());// Automatically use device culture
var localization = new DefaultDialogLocalization(CultureInfo.CurrentCulture);
DialogService.Instance.SetLocalization(localization);
// Force specific culture
var spanishLocalization = new DefaultDialogLocalization(new CultureInfo("es-ES"));
DialogService.Instance.SetLocalization(spanishLocalization);Extend BaseDialog to create custom dialogs with full theming support:
public class RatingDialog : BaseDialog
{
private readonly TaskCompletionSource<int> _taskCompletionSource = new();
private int _rating = 0;
public RatingDialog(string title, string message)
{
var grid = new Grid
{
Padding = 20,
RowDefinitions =
{
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto)
}
};
// Add title
grid.Add(CreateTitleLabel(title), 0, 0);
// Add message
grid.Add(CreateDescriptionLabel(message), 0, 1);
// Add star rating
var starsLayout = new HorizontalStackLayout
{
HorizontalOptions = LayoutOptions.Center,
Spacing = 10
};
for (int i = 1; i <= 5; i++)
{
var star = new Label
{
Text = "⭐",
FontSize = 30
};
int rating = i;
var tap = new TapGestureRecognizer();
tap.Tapped += (s, e) => SetRating(rating);
star.GestureRecognizers.Add(tap);
starsLayout.Children.Add(star);
}
grid.Add(starsLayout, 0, 2);
// Add buttons
var buttonLayout = new HorizontalStackLayout
{
HorizontalOptions = LayoutOptions.Center,
Spacing = 10
};
var submitButton = CreatePrimaryButton("Submit", OnSubmit);
var cancelButton = CreateSecondaryButton("Cancel", OnCancel);
buttonLayout.Children.Add(cancelButton);
buttonLayout.Children.Add(submitButton);
grid.Add(buttonLayout, 0, 3);
Content = CreateThemedFrame(grid);
}
private void SetRating(int rating)
{
_rating = rating;
// Update star display
}
private async void OnSubmit(object sender, EventArgs e)
{
_taskCompletionSource.TrySetResult(_rating);
await MopupService.Instance.PopAsync();
}
private async void OnCancel(object sender, EventArgs e)
{
_taskCompletionSource.TrySetResult(0);
await MopupService.Instance.PopAsync();
}
public async Task<int> ShowAsync()
{
await MopupService.Instance.PushAsync(this);
return await _taskCompletionSource.Task;
}
}// Register icons for specific dialog types
DialogService.Instance.RegisterCustomIcon(
DialogType.Custom,
"custom_icon_light.svg",
"custom_icon_dark.svg"
);
// Use in dialogs
await AlertDialog.ShowAsync(
"Custom Alert",
"This uses custom icons",
DialogType.Custom
);
// Per-instance custom icons
var dialog = new AlertDialog("Title", "Message")
{
CustomLightIcon = "special_light.png",
CustomDarkIcon = "special_dark.png"
};For comprehensive API documentation including all methods, properties, and examples, see API_REFERENCE.md
The central service for managing dialogs, themes, and localization.
// Access the singleton instance
var service = DialogService.Instance;
// Initialize with custom themes
service.Initialize(lightTheme, darkTheme);
// Theme management
service.CurrentThemeOverride = customTheme; // Override automatic detection
service.LightTheme; // Get light theme
service.DarkTheme; // Get dark theme
// Overlay settings
service.SetOverlayEnabled(true);
service.SetOverlayColor(Color.FromRgba("#80000000"));
// Localization
service.SetLocalization(new CustomLocalization());
// Custom icon registration
service.RegisterCustomIcon(DialogType.Custom, "light.png", "dark.png");public enum DialogType
{
None, // No icon
Info, // Information icon (blue)
Success, // Success icon (green)
Warning, // Warning icon (orange)
Error, // Error icon (red)
Question, // Question icon (purple)
Help, // Help icon
Custom // Custom icon (user-defined)
}| Property | Type | Default | Description |
|---|---|---|---|
BackgroundColor |
Color |
White/Dark | Dialog background |
OverlayColor |
Color |
#80000000 | Semi-transparent overlay |
ShowOverlay |
bool |
true | Enable background overlay |
TitleTextColor |
Color |
#212121 | Title text color |
TitleFontSize |
double |
18 | Title font size |
TitleMaxLines |
int |
2 | Maximum title lines |
TitleLineBreakMode |
LineBreakMode |
TailTruncation | Title truncation mode |
DescriptionTextColor |
Color |
#757575 | Description text color |
DescriptionFontSize |
double |
14 | Description font size |
DescriptionTextType |
TextType |
Text | Text or HTML rendering |
ButtonBackgroundColor |
Color |
#2196F3 | Primary button background |
ButtonTextColor |
Color |
White | Primary button text |
SecondaryButtonBackgroundColor |
Color |
#F5F5F5 | Secondary button background |
SecondaryButtonTextColor |
Color |
#212121 | Secondary button text |
DialogWidth |
double |
300 | Dialog width |
DialogHeight |
double |
250 | Dialog height |
DialogCornerRadius |
double |
12 | Corner radius |
DialogPadding |
double |
20 | Internal padding |
ButtonHeight |
double |
44 | Button height |
ButtonCornerRadius |
double |
8 | Button corner radius |
HasShadow |
bool |
true | Enable drop shadow |
AnimationDuration |
int |
250 | Animation duration (ms) |
EnableAnimation |
bool |
true | Enable/disable animations |
| Property | Type | Description |
|---|---|---|
Name |
string |
Display name |
Detail |
string? |
Optional description |
Value |
int |
Return value when selected |
Action |
Action? |
Sync callback invoked on selection (v1.4.1+) |
AsyncAction |
Func<Task>? |
Async callback invoked on selection (v1.4.1+) |
ImageDark |
string? |
Dark theme icon path |
ImageLight |
string? |
Light theme icon path |
SubItems |
List<ActionItem>? |
Hierarchical sub-menu items |
ItemId |
Guid |
Unique identifier |
ShowImage |
bool |
Whether to show icon (computed) |
HasDetail |
bool |
Whether detail text exists (computed) |
HasSubItems |
bool |
Whether sub-items exist (computed) |
HasAction |
bool |
Whether a callback is defined (computed, v1.4.1+) |
| Platform | Minimum Version |
|---|---|
| .NET | 9.0 |
| iOS | 11.0 |
| Android | API 21 (5.0 Lollipop) |
| Windows | 10.0.17763.0 (1809) |
| macOS | 13.1 (via Mac Catalyst) |
- Microsoft.Maui.Controls (9.0.110+)
- Microsoft.Maui.Controls.Compatibility (9.0.110+)
- Mopups (1.3.4+) - Automatically included
No breaking changes. New features are additive:
// NEW: Date/Time Pickers
DateTime? date = await DatePickerDialog.ShowAsync("Select Date");
TimeSpan? time = await TimePickerDialog.ShowAsync("Select Time");
DateTime? dateTime = await DateTimePickerDialog.ShowAsync("Select Date & Time");
// NEW: Design System Presets
DialogService.Instance.CurrentThemeOverride = DialogTheme.Material();
DialogService.Instance.CurrentThemeOverride = DialogTheme.Fluent();
DialogService.Instance.CurrentThemeOverride = DialogTheme.Cupertino();
// NEW: Logging (optional)
DialogService.Instance.Logger = DebugDialogLogger.Instance;
// NEW: ButtonCornerRadius theme property
var theme = DialogTheme.LightTheme.Clone();
theme.ButtonCornerRadius = 20; // Pill-shaped buttonsNo breaking changes. New features are additive:
// NEW: ActionItem with callbacks - no more switch statements!
var actions = new List<ActionItem>
{
new ActionItem("Edit", () => EditItem(), "Edit this item"),
new ActionItem("Delete", async () => await DeleteItemAsync(), "Remove permanently")
};
// NEW: ShowWithActionsAsync for action-based dialogs
bool wasSelected = await ActionListDialog.ShowWithActionsAsync("Actions", actions);
// NEW: Toast horizontal positioning
await Toast.ShowAsync("Saved!", DialogType.Success, ToastDuration.Short,
ToastPosition.Bottom, ToastHorizontalPosition.Right);No breaking changes. New features are additive:
// NEW: Toast notifications
await Toast.ShowAsync("Message sent", DialogType.Success);
// NEW: Snackbar with action
var result = await Snackbar.ShowAsync("Item deleted", "UNDO", () => RestoreItem());
// NEW: Configure stacking behavior
Toast.DefaultStackBehavior = ToastStackBehavior.Stack;
Snackbar.DefaultStackBehavior = SnackbarStackBehavior.Replace;No breaking changes. New features are additive:
// NEW: Hierarchical menus
var item = new ActionItem("Menu", "Description", 0);
item.SubItems = new List<ActionItem> { /* sub-items */ };No breaking changes. New theme properties:
// NEW: Title customization
theme.TitleMaxLines = 2;
theme.TitleLineBreakMode = LineBreakMode.TailTruncation;
// NEW: HTML support
theme.DescriptionTextType = TextType.Html;No breaking changes. New ActionListDialog features:
// NEW: Multi-line descriptions
var dialog = new ActionListDialog(
title, actions, cancelText,
customHeight: null,
descriptionMaxLines: 2, // NEW
descriptionLineBreakMode: LineBreakMode.TailTruncation // NEW
);
// NEW: Dynamic updates
dialog.DescriptionMaxLines = 3;
dialog.DescriptionLineBreakMode = LineBreakMode.WordWrap;// Overlay configuration
DialogService.Instance.SetOverlayEnabled(true);
DialogService.Instance.SetOverlayColor(Color.FromRgba("#80000000"));
// Theme management
DialogService.Instance.CurrentThemeOverride = customTheme;
// Localization
DialogService.Instance.SetLocalization(customLocalization);
// Custom icons
DialogService.Instance.RegisterCustomIcon(type, lightIcon, darkIcon);var dialog = new AlertDialog("Title", "Message")
{
// Custom padding for description
DescriptionPadding = new Thickness(20, 10),
// Custom icons
CustomLightIcon = "icon_light.png",
CustomDarkIcon = "icon_dark.png"
};Problem: Dialog doesn't appear when calling ShowAsync().
Solution: Ensure Mopups is configured in MauiProgram.cs:
builder.ConfigureMopups();Problem: Dialog icons appear blank or missing.
Solution:
- Verify icon files are included in the project as
MauiImage - Check that file names match exactly (case-sensitive)
- For Windows, ensure PNG format is used
Problem: Custom theme changes aren't reflected.
Solution:
// Clear override to use automatic detection
DialogService.Instance.CurrentThemeOverride = null;
// Or explicitly set the theme
DialogService.Instance.CurrentThemeOverride = customTheme;Problem: App receives memory warnings with many dialogs.
Solution: The library includes automatic image caching. Ensure you're not creating redundant dialog instances.
Problem: HeadTruncation or MiddleTruncation don't work on Windows.
Solution: Use platform-compatible modes:
theme.TitleLineBreakMode = LineBreakMode.TailTruncation; // Recommended
// or
theme.TitleLineBreakMode = LineBreakMode.WordWrap;Problem: Exception when showing dialog immediately after another dismisses.
Solution: This was fixed in v1.3.0. Upgrade to latest version or ensure proper async/await usage:
var result = await actionListDialog.ShowAsync();
// Dialog is fully dismissed before this line executes
await nextDialog.ShowAsync(); // Safe to show next dialogiOS
- Supports all features including gesture recognizers
- Memory-optimized image loading
Android
- Full material design integration
- Hardware back button support in hierarchical menus
Windows
- Some
LineBreakModeoptions have limited support - Recommend using
TailTruncationorWordWrap
macOS (Catalyst)
- Keyboard navigation supported
- Dark mode follows system preferences
We welcome contributions from the community.
-
Clone the repository:
git clone https://github.com/MarketAlly/Dialogs.Maui.git
-
Open solution in Visual Studio 2022 or JetBrains Rider
-
Restore NuGet packages:
dotnet restore
-
Build the solution:
dotnet build
-
Fork the Repository: Create your own fork on GitHub
-
Create a Feature Branch:
git checkout -b feature/your-feature-name
-
Follow Code Standards:
- Use consistent naming conventions
- Add XML documentation for public APIs
- Include unit tests for new features
-
Commit with Clear Messages:
git commit -m "Add: Brief description of feature" -
Push and Create PR:
git push origin feature/your-feature-name
Then open a Pull Request on GitHub
-
PR Requirements:
- Clear description of changes
- No breaking changes without discussion
- All tests passing
- Updated documentation if needed
- Use C# 12 features where appropriate
- Enable nullable reference types
- Follow .NET naming conventions
- Keep methods focused and small
- GitHub Issues: Report bugs or request features
- Documentation: Full API documentation in this README
- Email: support@marketally.com
When reporting bugs, please include:
- .NET MAUI version
- Target platform(s) affected
- Minimal reproduction code
- Expected vs. actual behavior
- Stack trace (if applicable)
This project is licensed under the MIT License.
MIT License
Copyright (c) 2025 MarketAlly
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Built on .NET MAUI by Microsoft
- Popup infrastructure powered by Mopups by LuckyDucko
- Inspired by Material Design principles
New Features:
- Date Picker Dialog: Native date selection with Today button and min/max constraints
- Time Picker Dialog: Native time selection with Now button
- DateTime Picker Dialog: Combined date and time selection with responsive platform-specific layouts
- Horizontal layout on Android/iOS (date and time on same row)
- Vertical layout on Windows/macOS (stacked)
- Design System Presets: Pre-built themes following popular design systems
DialogTheme.Material()/DialogTheme.MaterialDark()- Google Material Design 3DialogTheme.Fluent()/DialogTheme.FluentDark()- Microsoft Fluent DesignDialogTheme.Cupertino()/DialogTheme.CupertinoDark()- Apple Human Interface Guidelines
- Custom Animation Effects: Slide, fade, scale, and combined transitions
DialogAnimationTypeenum with None, Fade, Scale, ScaleBounce, SlideUp/Down/Left/Right, FadeScale, SlideFadeIDialogAnimationinterface for custom animations
- Accessibility Improvements: Enhanced screen reader and automation support
AccessibilityHelperfor semantic properties, announcements, and WCAG contrast checkingAccessibilitySettingsfor configuring accessibility behavior
- Performance Telemetry: Optional analytics for dialog usage
IDialogTelemetryinterface for custom telemetry providers- Track dialog open/close events with timing data
- Additional Localizations: Chinese, Japanese, Portuguese, Italian
- Now 8 languages total (English, Spanish, French, German, Chinese, Japanese, Portuguese, Italian)
- MVVM Command Binding: ICommand support via
RelayCommandandDialogCommands- Pre-built commands for common dialog actions
- Input Validation Framework: Built-in validators for Prompt/Editor
ValidationResultandValidationOptionsclasses- Real-time, on-submit, and on-blur validation triggers
- WCAG-compliant error styling
- ButtonCornerRadius Theme Property: Control button corner radius independently from dialog
- IDialogLogger Interface: Optional logging support for debugging and monitoring
DebugDialogLoggerfor development debuggingNullDialogLogger(default) for production
Production Improvements:
- IDisposable Pattern: BaseDialog now implements IDisposable with proper event cleanup
- Thread-Safe DialogService: All properties and methods are now synchronized for concurrent access
- Exception Handling: Proper exception logging instead of silent swallowing
- Memory Management: CancellationTokenSource disposal in Toast/Snackbar
New Features:
- ActionItem Action Callbacks: Define
ActionorAsyncActiondirectly onActionItemfor automatic invocationnew ActionItem("Name", () => DoSomething(), "description")for sync callbacksnew ActionItem("Name", async () => await DoAsync(), "description")for async callbacks- Eliminates need for switch statements on returned values
- ActionListDialog.ShowWithActionsAsync(): New method returning
boolfor action-based dialogs - Toast Horizontal Positioning: Position toasts in corners or edges of screen
ToastHorizontalPositionenum (Left, Center, Right)- Combine with
ToastPosition(Top, Bottom) for 6 position options
Improvements:
ActionItem.InvokeActionAsync()method for manual action invocationActionItem.HasActionproperty to check if callback is definedAsyncActiontakes precedence overActionwhen both are set- Corner-positioned toasts appear instantly (no slide-from-center animation)
New Features:
- Toast Notifications: Lightweight, non-interactive notifications for quick status updates
- Configurable vertical position (Top/Bottom)
- Short (2s) and Long (3.5s) durations, or custom milliseconds
- Optional icons using existing DialogType
- Configurable stacking behavior (Stack, Replace, Queue)
- Snackbar Notifications: Actionable notifications with optional buttons
- Action button with callback support (UNDO, RETRY, VIEW, etc.)
- Short (4s), Long (7s), or Indefinite duration
- Swipe-to-dismiss support
- Returns SnackbarResult (ActionClicked, Dismissed, TimedOut)
- Configurable stacking behavior
- New Localization Strings: Added DISMISS, UNDO, RETRY translations for all 4 languages
Improvements:
- Non-blocking notifications allow continued user interaction
- Multiple notifications can stack vertically
- Consistent theming with existing dialog components
New Features:
- Hierarchical Action List Support with
ActionItem.SubItems - Automatic back navigation in sub-menus
- Unlimited nesting depth for menu hierarchies
Bug Fixes:
- Fixed critical "duplicate key" exception on rapid dialog transitions
- Fixed async race condition in
PopAsynchandling
New Features:
- Title MaxLines and LineBreakMode configuration
- HTML description support via
DescriptionTextType
New Features:
- Multi-line description support in ActionListDialog
- Configurable line break modes
- Dynamic property updates after dialog creation
- Intelligent scrolling for content overflow
Improvements:
- Fixed dialog height with scrollable content
- Instant dismissal option via
EnableAnimation - Double-tap prevention
Initial Release:
- 7 dialog types (Alert, Confirm, Prompt, Editor, Loading, ActionList, ColorPicker)
- Dark/light theme support with automatic detection
- Internationalization (English, Spanish, French, German)
- Custom icon support
- Cross-platform support (iOS, Android, Windows, macOS)
All planned features have been implemented! 🎉
- Snackbar/Toast notifications - Non-blocking notifications ✅ Added in v1.4.0
- Date/Time picker dialogs - Native date and time selection ✅ Added in v1.5.0
- Preset theme gallery - Material, Fluent, Cupertino themes ✅ Added in v1.5.0
- Custom animation effects - Slide, fade, scale transitions ✅ Added in v1.5.0
- Accessibility improvements - Enhanced screen reader support ✅ Added in v1.5.0
- Performance telemetry - Optional analytics for dialog usage ✅ Added in v1.5.0
- Additional localizations - Chinese, Japanese, Portuguese, Italian ✅ Added in v1.5.0
- MVVM command binding - ICommand support for button actions ✅ Added in v1.5.0
- Input validation framework - Built-in validators for Prompt/Editor ✅ Added in v1.5.0
Built with precision by MarketAlly
Enterprise-grade dialog solutions for .NET MAUI applications.