Skip to content

IChoiceDialogService

IChoiceDialogService provides a platform-agnostic way to show a modal dialog with an arbitrary set of buttons and return the result of the pressed one. The dialog result can be a string, bool, enumeration value, or any other type. From your ViewModel, you can use IChoiceDialogService to display a dialog and receive a strongly typed answer, without referencing any window type.

Main features include:

  • Dialog is automatically closed when one of its choice buttons (shown in the dialog's footer) is clicked.
  • Any number of choice buttons — For each button you can specify a caption and associate it with a dialog result, returned when this button is pressed.
  • Dialog result — The dialog result is specified by a clicked button's Result value, which can be a string, a bool, an enumeration, or any other type.
  • Customizable button placement — Dialog buttons support the Dock property which allows you to forcibly place buttons first or last.
  • Default button — You can mark a specific button as default. When the user presses ENTER, the dialog invokes this button's command.
  • Cancel button — Mark any button with IsCancel to make it respond to ESC.
  • Automatic owner — The owner window is resolved by the underlying IDialogService through IWindowsManager, so it does not need to be specified explicitly.
  • Choice buttons are always enabled — Unlike IDialogService, which supports CanOk for validation, IChoiceDialogService has no mechanism to disable individual choices based on input state.

Interface Definition

public interface IChoiceDialogService
{
    // Show a modal dialog with the given choices.
    T Show<T>(string message, string? caption, IReadOnlyList<DialogChoice<T>> choices);
}
Parameter Type Description
message string The dialog's body content assigned to the dialog ViewModel's Content property. Cannot be null.
caption string? The dialog's caption. When set to null, the caption is taken from the dialog ViewModel's Title property.
choices IReadOnlyList<DialogChoice<T>> The available choices displayed as buttons in the dialog's footer. The order matches the button order. Cannot be null and must contain at least one item.

Returns:

  • T — the result associated with the clicked choice button
  • default(T) if the dialog was closed without a choice by clicking the dialog's caption 'x' button. default(T) is null for reference types (including string), false for the bool type, 0 for an enum without an explicit zero member.
  • Pressing ENTER — If the dialog contains a choice button with IsDefault = true, pressing ENTER returns that button's Result value. If no choice button has IsDefault = true, pressing ENTER does nothing, and the dialog remains open.
  • Pressing ESC — If the dialog contains a choice button with IsCancel = true, pressing Escape returns that button's Result value. If no choice button has IsCancel = true, pressing Escape does nothing, and the dialog remains open.

See also: Handle a Dismissed Dialog.

Exceptions:

  • ArgumentNullException — The message or choices parameter is null.
  • ArgumentException — The choices list is empty. A dialog must contain at least one button.

DialogChoice

Each choice button in the dialog is described by a DialogChoice<T> instance:

public sealed class DialogChoice<T>
{
    // The button caption.
    public string Caption { get; }

    // The result returned by the `IChoiceDialogService.Show` method when the button is pressed.
    public T Result { get; }

    // Whether the current button is the default button, invoked by Enter.
    public bool IsDefault { get; set; }

    // Whether the current button is the cancel button, invoked by Escape.
    public bool IsCancel { get; set; }

    // The side to which the button is docked. Defaults to `DockType.Right`.
    public DockType Dock { get; set; } = DockType.Right;

    // The button order within its side. Use the `Index` property when you need to reorder buttons without changing the button array.
    public int? Index { get; set; }

    public DialogChoice(string caption, T result);
}

How to Use IChoiceDialogService

In your ViewModel, call the IChoiceDialogService.Show method to display the dialog with specified choice buttons. The method returns the Result value of the pressed choice button.

Access the Service

There are two ways to access an IChoiceDialogService object in a ViewModel:

  • Through the Service<T>() helper method. This is a convenient approach to obtain registered application services shipped with the Eremex Controls library.
  • Through constructor injection.

Service() Helper

Implement a helper Service<T>() method in your ViewModel to get a requested service using the static ApplicationServicesContext.GetRequiredService<T>() method.

Example - Show a Dialog Using Service() Helper
using Eremex.AvaloniaUI.Controls.ApplicationServices;
using CommunityToolkit.Mvvm.Input;

public partial class MyViewModel : ObservableObject
{
    // Provides access to any registered service
    protected static T Service<T>() where T : class
        => ApplicationServicesContext.GetRequiredService<T>();

    [RelayCommand]
    private void ShowChoiceDialog()
    {
        // Resolve the service and show the dialog
        var result = Service<IChoiceDialogService>().Show(
            "The license server did not respond.",
            "Activation failed",
            new[]
            {
                new DialogChoice<string>("Show log", "log") { IsDefault = true },
                new DialogChoice<string>("Retry", "retry"),
                new DialogChoice<string>("Close", "close") { IsCancel = true },
            });
        if(result == "retry") 
        {
            //...
        }
        else 
        {
            //...
        }
    }
}

In the App.axaml.cs file, ensure that Eremex application services are registered using SimpleServiceProvider and ApplicationServicesContext as follows:

public class App : Application
{
    public override void OnFrameworkInitializationCompleted()
    {
        RegisterApplicationServices();
        //...
    }

    static void RegisterApplicationServices()
    {
        // Register built-in services.
        var serviceProvider = new SimpleServiceProvider();
        ApplicationServicesContext.RegisterApplicationServices(serviceProvider.AddSingleton);
        ApplicationServicesContext.SetCurrent(serviceProvider);
    }
}

Constructor Injection

Implement a constructor in your ViewModel with IChoiceDialogService as a parameter. When you instantiate the ViewModel, pass the service object to this constructor.

public partial class MyViewModel
{
    private readonly IChoiceDialogService _choiceDialogService;

    public MyViewModel(IChoiceDialogService choiceDialogService)
    {
        _choiceDialogService = choiceDialogService;
    }

    [RelayCommand]
    private void ShowChoiceDialog()
    {
        var result = _choiceDialogService.Show(
            "The license server did not respond.",
            "Activation failed",
            new[]
            {
                new DialogChoice<string>("Show log", "log") { IsDefault = true },
                new DialogChoice<string>("Retry", "retry"),
                new DialogChoice<string>("Close", "close") { IsCancel = true },
            });
        //...
    }
}

Return a String Result

To return a string value when a dialog button is clicked, use DialogChoice<string> for that button. The value associated with this button is specified by the result parameter of the DialogChoice<T> constructor.

The following example shows a dialog with three buttons. Specific dialog results (string values — "log", "retry", and "close") are associated with these buttons. The dialog's Show method returns a corresponding value when one of its buttons is clicked.

[RelayCommand]
private void ShowDialogWithCustomCaptions()
{
    var result = Service<IChoiceDialogService>().Show(
        DialogMessage,
        DialogTitle,
        new[]
        {
            new DialogChoice<string>("Show log", "log") { IsDefault = true },
            new DialogChoice<string>("Retry", "retry"),
            new DialogChoice<string>("Close", "close") { IsCancel = true },
        });
}

Return a Boolean Result

To return a Boolean value when a dialog button is clicked, use DialogChoice<bool> for that button. The value associated with this button is specified by the result parameter of the DialogChoice<T> constructor.

The following example shows a dialog with two buttons. Specific dialog results (true and false) are associated with these buttons. The dialog's Show method returns a corresponding value when one of its buttons is clicked.

[RelayCommand]
private void ShowDialogWithBooleanResult()
{
    var result = Service<IChoiceDialogService>().Show(
        DialogMessage,
        DialogTitle,
        new[]
        {
            new DialogChoice<bool>("Show log", true) { IsDefault = true },
            new DialogChoice<bool>("Close", false) { IsCancel = true },
        });
}

See also: Handle a Dismissed Dialog.

Return an Enum Result

To return an enumeration value when a dialog button is clicked, use DialogChoice< enum > for that button. The value associated with this button is specified by the result parameter of the DialogChoice<T> constructor.

The following example shows a dialog with three buttons. Specific dialog results (DialogResult.Abort, DialogResult.Retry, and DialogResult.Ignore) are associated with these buttons. The dialog's Show method returns a corresponding value when one of its buttons is clicked.

[RelayCommand]
private void ShowDialogWithEnumResult()
{
    var result = Service<IChoiceDialogService>().Show(
        DialogMessage,
        DialogTitle,
        new[]
        {
            new DialogChoice<DialogResult>("Abort", DialogResult.Abort) { IsCancel = true },
            new DialogChoice<DialogResult>("Retry", DialogResult.Retry) { IsDefault = true },
            new DialogChoice<DialogResult>("Ignore", DialogResult.Ignore),
        });
}

Set Button Placement

Use the DialogChoice<T>.Dock property to forcibly position specific buttons first (set the Dock property to DockType.Left) or last (set the Dock property to DockType.Right) among all buttons. The Dock property defaults to DockType.Right.

[RelayCommand]
private void ShowDialogWithCustomButtonPlacement()
{
    var result = Service<IChoiceDialogService>().Show(
        DialogMessage,
        DialogTitle,
        new[]
        {
            new DialogChoice<string>("Help", "help") { Dock = DockType.Left },
            new DialogChoice<string>("Retry", "retry") { IsDefault = true },
            new DialogChoice<string>("Cancel", "cancel") { IsCancel = true },
        });
}

Handle a Dismissed Dialog

When the user dismisses the dialog by clicking the caption 'x' button, the Show method returns default(T):

  • For reference types (string, etc.), the result is null.
  • For value types (bool, enums, etc.), the result is the default value of the type (false for a Boolean type, 0 for an enumeration, etc.)

If the dialog contains a cancel button (a choice button whose IsCancel property is true), clicking it or pressing Escape closes the dialog with that button's result. The caption 'x' button still closes the dialog with the default(T) result.

Example — Handle a Dialog's Dismissal

The following example creates a dialog with a cancel button associated with the null dialog result. In this case, you only need to check for the null dialog result to handle all dialog dismissal scenarios:

  • The cancel button is clicked.
  • The x caption button is clicked.
  • The Escape key is pressed.
var result = Service<IChoiceDialogService>().Show(
    "Save the document?",
    "Confirmation",
    new[]
    {
        new DialogChoice<bool?>("Yes", true) { IsDefault = true },
        new DialogChoice<bool?>("No", false),
        new DialogChoice<bool?>("Cancel", null) { IsCancel = true },
    });

if (result is null)
{
    // Dialog was dismissed or Cancel was clicked, or Escape was pressed.
}
else if (result.Value)
{
    // Yes was pressed.
}
else
{
    // No was pressed.
}