Skip to content

MarketAlly/Dialogs.Maui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MarketAlly.Dialogs.Maui

NuGet Version NuGet Downloads License: MIT .NET Platform

A comprehensive, production-ready dialog library for .NET MAUI applications with built-in theming, localization, hierarchical menus, and extensive customization options.

Table of Contents

Features

Core Capabilities

  • 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+

Technical Highlights

  • 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 .snupkg packages

Installation

Install via NuGet Package Manager:

dotnet add package MarketAlly.Dialogs.Maui

Or via Package Manager Console:

Install-Package MarketAlly.Dialogs.Maui

Or search for MarketAlly.Dialogs.Maui in the NuGet Package Manager UI.

Quick Start

1. Configure in MauiProgram.cs

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();
}

2. Initialize (Optional)

// 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"));
}

3. Show Your First Dialog

using MarketAlly.Dialogs.Maui.Dialogs;

// Simple alert
await AlertDialog.ShowAsync("Welcome!", "Thanks for using our dialogs", DialogType.Success);

Dialog Types

Alert Dialog

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();

Confirm Dialog

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();

Prompt Dialog

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
);

Editor Dialog

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();

Loading Dialog

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);

Action List Dialog

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!

Color Picker Dialog

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
);

Date Picker Dialog (v1.5.0+)

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);

Time Picker Dialog (v1.5.0+)

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);

DateTime Picker Dialog (v1.5.0+)

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

Notifications

Toast

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;

Snackbar

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
}

When to Use Toast vs Snackbar

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

Theming

The library provides comprehensive theming support with automatic dark/light mode detection.

Design System Presets (v1.5.0+)

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

Custom Theme Creation

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);

Dynamic Theme Switching

// 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"));

Title Customization

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:

  • HeadTruncation and MiddleTruncation may not render correctly on Windows due to MAUI framework limitations
  • For cross-platform compatibility, use TailTruncation (default) or WordWrap

Description Text Type (HTML Support)

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

Localization

The library includes a complete localization framework with built-in translations and extensibility.

Built-in Language Support

  • English (default)
  • Spanish (es)
  • French (fr)
  • German (de)

Custom Localization Implementation

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());

Culture-Based Automatic Localization

// 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);

Advanced Features

Custom Dialog Creation

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;
    }
}

Custom Icon Registration

// 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"
};

API Reference

For comprehensive API documentation including all methods, properties, and examples, see API_REFERENCE.md

DialogService (Singleton)

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");

DialogType Enumeration

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)
}

DialogTheme Properties

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

ActionItem Properties

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+)

Requirements

Minimum Platform Versions

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)

Dependencies

  • Microsoft.Maui.Controls (9.0.110+)
  • Microsoft.Maui.Controls.Compatibility (9.0.110+)
  • Mopups (1.3.4+) - Automatically included

Migration Guide

Upgrading from v1.4.x to v1.5.0

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 buttons

Upgrading from v1.4.0 to v1.4.1

No 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);

Upgrading from v1.3.x to v1.4.0

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;

Upgrading from v1.2.x to v1.3.0

No breaking changes. New features are additive:

// NEW: Hierarchical menus
var item = new ActionItem("Menu", "Description", 0);
item.SubItems = new List<ActionItem> { /* sub-items */ };

Upgrading from v1.1.x to v1.2.0

No breaking changes. New theme properties:

// NEW: Title customization
theme.TitleMaxLines = 2;
theme.TitleLineBreakMode = LineBreakMode.TailTruncation;

// NEW: HTML support
theme.DescriptionTextType = TextType.Html;

Upgrading from v1.0.x to v1.1.0

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;

Configuration Options

Global Settings

// 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);

Per-Dialog Settings

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"
};

Troubleshooting

Common Issues

Dialog Not Showing

Problem: Dialog doesn't appear when calling ShowAsync().

Solution: Ensure Mopups is configured in MauiProgram.cs:

builder.ConfigureMopups();

Icons Not Displaying

Problem: Dialog icons appear blank or missing.

Solution:

  1. Verify icon files are included in the project as MauiImage
  2. Check that file names match exactly (case-sensitive)
  3. For Windows, ensure PNG format is used

Theme Not Applying

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;

Memory Warnings on iOS

Problem: App receives memory warnings with many dialogs.

Solution: The library includes automatic image caching. Ensure you're not creating redundant dialog instances.

Title Truncation Issues on Windows

Problem: HeadTruncation or MiddleTruncation don't work on Windows.

Solution: Use platform-compatible modes:

theme.TitleLineBreakMode = LineBreakMode.TailTruncation; // Recommended
// or
theme.TitleLineBreakMode = LineBreakMode.WordWrap;

ActionListDialog "Duplicate Key" Exception

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 dialog

Platform-Specific Notes

iOS

  • Supports all features including gesture recognizers
  • Memory-optimized image loading

Android

  • Full material design integration
  • Hardware back button support in hierarchical menus

Windows

  • Some LineBreakMode options have limited support
  • Recommend using TailTruncation or WordWrap

macOS (Catalyst)

  • Keyboard navigation supported
  • Dark mode follows system preferences

Contributing

We welcome contributions from the community.

Development Setup

  1. Clone the repository:

    git clone https://github.com/MarketAlly/Dialogs.Maui.git
  2. Open solution in Visual Studio 2022 or JetBrains Rider

  3. Restore NuGet packages:

    dotnet restore
  4. Build the solution:

    dotnet build

Contribution Guidelines

  1. Fork the Repository: Create your own fork on GitHub

  2. Create a Feature Branch:

    git checkout -b feature/your-feature-name
  3. Follow Code Standards:

    • Use consistent naming conventions
    • Add XML documentation for public APIs
    • Include unit tests for new features
  4. Commit with Clear Messages:

    git commit -m "Add: Brief description of feature"
  5. Push and Create PR:

    git push origin feature/your-feature-name

    Then open a Pull Request on GitHub

  6. PR Requirements:

    • Clear description of changes
    • No breaking changes without discussion
    • All tests passing
    • Updated documentation if needed

Code Style

  • Use C# 12 features where appropriate
  • Enable nullable reference types
  • Follow .NET naming conventions
  • Keep methods focused and small

Support

Getting Help

Reporting Issues

When reporting bugs, please include:

  1. .NET MAUI version
  2. Target platform(s) affected
  3. Minimal reproduction code
  4. Expected vs. actual behavior
  5. Stack trace (if applicable)

License

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.

Acknowledgments

  • Built on .NET MAUI by Microsoft
  • Popup infrastructure powered by Mopups by LuckyDucko
  • Inspired by Material Design principles

Changelog

Version 1.5.0 (Latest)

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 3
    • DialogTheme.Fluent() / DialogTheme.FluentDark() - Microsoft Fluent Design
    • DialogTheme.Cupertino() / DialogTheme.CupertinoDark() - Apple Human Interface Guidelines
  • Custom Animation Effects: Slide, fade, scale, and combined transitions
    • DialogAnimationType enum with None, Fade, Scale, ScaleBounce, SlideUp/Down/Left/Right, FadeScale, SlideFade
    • IDialogAnimation interface for custom animations
  • Accessibility Improvements: Enhanced screen reader and automation support
    • AccessibilityHelper for semantic properties, announcements, and WCAG contrast checking
    • AccessibilitySettings for configuring accessibility behavior
  • Performance Telemetry: Optional analytics for dialog usage
    • IDialogTelemetry interface 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 RelayCommand and DialogCommands
    • Pre-built commands for common dialog actions
  • Input Validation Framework: Built-in validators for Prompt/Editor
    • ValidationResult and ValidationOptions classes
    • 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
    • DebugDialogLogger for development debugging
    • NullDialogLogger (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

Version 1.4.1

New Features:

  • ActionItem Action Callbacks: Define Action or AsyncAction directly on ActionItem for automatic invocation
    • new ActionItem("Name", () => DoSomething(), "description") for sync callbacks
    • new ActionItem("Name", async () => await DoAsync(), "description") for async callbacks
    • Eliminates need for switch statements on returned values
  • ActionListDialog.ShowWithActionsAsync(): New method returning bool for action-based dialogs
  • Toast Horizontal Positioning: Position toasts in corners or edges of screen
    • ToastHorizontalPosition enum (Left, Center, Right)
    • Combine with ToastPosition (Top, Bottom) for 6 position options

Improvements:

  • ActionItem.InvokeActionAsync() method for manual action invocation
  • ActionItem.HasAction property to check if callback is defined
  • AsyncAction takes precedence over Action when both are set
  • Corner-positioned toasts appear instantly (no slide-from-center animation)

Version 1.4.0

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

Version 1.3.0

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 PopAsync handling

Version 1.2.0

New Features:

  • Title MaxLines and LineBreakMode configuration
  • HTML description support via DescriptionTextType

Version 1.1.0

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

Version 1.0.0

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)

Roadmap

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.