Перейти к содержанию

IChoiceDialogService

IChoiceDialogService предоставляет платформенно-независимый способ показать модальный диалог с произвольным набором кнопок и вернуть результат нажатой кнопки. Результатом диалога может быть string, bool, значение перечисления или любой другой тип. С помощью IChoiceDialogService ViewModel может отобразить диалог и получить строго типизированный ответ, не ссылаясь ни на какой тип окна.

Основные возможности:

  • Диалог автоматически закрывается при нажатии одной из его кнопок выбора (расположенных в нижней части диалога).
  • Любое количество кнопок выбора — Для каждой кнопки можно задать подпись и связать её с результатом диалога, который возвращается при нажатии этой кнопки.
  • Результат диалога — Результат диалога определяется значением Result нажатой кнопки, которое может быть string, bool, перечислением или любым другим типом.
  • Настраиваемое расположение кнопок — Кнопки диалога поддерживают свойство Dock, которое позволяет принудительно разместить кнопки первыми или последними.
  • Кнопка по умолчанию — Любую кнопку можно отметить как кнопку по умолчанию. Когда пользователь нажимает ENTER, диалог вызывает команду этой кнопки.
  • Кнопка отмены — Отметьте любую кнопку с помощью IsCancel, чтобы она реагировала на ESC.
  • Автоматический владелец — Окно-владелец определяется базовым сервисом IDialogService через IWindowsManager, поэтому его не нужно задавать явно.
  • Кнопки выбора всегда доступны — В отличие от IDialogService, который поддерживает CanOk для валидации, в IChoiceDialogService нет механизма, позволяющего отключать отдельные варианты выбора в зависимости от состояния ввода.

Определение интерфейса

public interface IChoiceDialogService
{
    // Show a modal dialog with the given choices.
    T Show<T>(string message, string? caption, IReadOnlyList<DialogChoice<T>> choices);
}
Параметр Тип Описание
message string Основное содержимое диалога, присваиваемое свойству Content ViewModel диалога. Не может быть null.
caption string? Заголовок диалога. Если задано значение null, заголовок берётся из свойства Title ViewModel диалога.
choices IReadOnlyList<DialogChoice<T>> Доступные варианты выбора, отображаемые в виде кнопок в нижней части диалога. Порядок соответствует порядку кнопок. Не может быть null и должен содержать хотя бы один элемент.

Возвращаемое значение:

  • T — результат, связанный с нажатой кнопкой выбора
  • default(T), если диалог был закрыт без выбора нажатием кнопки закрытия ('x') диалога. default(T) равно null для ссылочных типов (включая string), false для типа bool, 0 для перечисления без явного нулевого члена.
  • Нажатие ENTER — Если в диалоге есть кнопка выбора с IsDefault = true, нажатие ENTER возвращает значение Result этой кнопки. Если ни у одной кнопки выбора нет IsDefault = true, нажатие ENTER ничего не делает, и диалог остаётся открытым.
  • Нажатие ESC — Если в диалоге есть кнопка выбора с IsCancel = true, нажатие Escape возвращает значение Result этой кнопки. Если ни у одной кнопки выбора нет IsCancel = true, нажатие Escape ничего не делает, и диалог остаётся открытым.

См. также: Обработка закрытия диалога без выбора.

Исключения:

  • ArgumentNullException — Параметр message или choices равен null.
  • ArgumentException — Список choices пуст. Диалог должен содержать хотя бы одну кнопку.

DialogChoice

Каждая кнопка выбора в диалоге описывается экземпляром DialogChoice<T>:

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

Как использовать IChoiceDialogService

В ViewModel вызовите метод IChoiceDialogService.Show, чтобы отобразить диалог с указанными кнопками выбора. Метод возвращает значение Result нажатой кнопки выбора.

Доступ к сервису

Получить объект IChoiceDialogService в ViewModel можно двумя способами:

  • С помощью вспомогательного метода Service<T>(). Это удобный способ получить зарегистрированные сервисы приложения, входящие в библиотеку Eremex Controls.
  • Через внедрение в конструктор.

Вспомогательный метод Service()

Реализуйте в ViewModel вспомогательный метод Service<T>(), который получает запрошенный сервис с помощью статического метода ApplicationServicesContext.GetRequiredService<T>().

Пример - отображение диалога с помощью вспомогательного метода Service()
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 
        {
            //...
        }
    }
}

Убедитесь, что в файле App.axaml.cs сервисы приложения Eremex зарегистрированы с помощью SimpleServiceProvider и ApplicationServicesContext следующим образом:

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

Внедрение в конструктор

Реализуйте в ViewModel конструктор с параметром типа IChoiceDialogService. При создании ViewModel передайте объект сервиса в этот конструктор.

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

Возврат строкового результата

Чтобы при нажатии кнопки диалога возвращалось строковое значение, используйте для этой кнопки DialogChoice<string>. Значение, связанное с кнопкой, задаётся параметром result конструктора DialogChoice<T>.

В этом примере показан диалог с тремя кнопками. С этими кнопками связаны определённые результаты диалога (строковые значения "log", "retry" и "close"). Метод Show диалога возвращает соответствующее значение при нажатии одной из его кнопок.

[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 },
        });
}

Возврат логического результата

Чтобы при нажатии кнопки диалога возвращалось логическое значение, используйте для этой кнопки DialogChoice<bool>. Значение, связанное с кнопкой, задаётся параметром result конструктора DialogChoice<T>.

В этом примере показан диалог с двумя кнопками. С этими кнопками связаны определённые результаты диалога (true и false). Метод Show диалога возвращает соответствующее значение при нажатии одной из его кнопок.

[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 },
        });
}

См. также: Обработка закрытия диалога без выбора.

Возврат значения перечисления

Чтобы при нажатии кнопки диалога возвращалось значение перечисления, используйте для этой кнопки DialogChoice< enum >. Значение, связанное с кнопкой, задаётся параметром result конструктора DialogChoice<T>.

В этом примере показан диалог с тремя кнопками. С этими кнопками связаны определённые результаты диалога (DialogResult.Abort, DialogResult.Retry и DialogResult.Ignore). Метод Show диалога возвращает соответствующее значение при нажатии одной из его кнопок.

[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),
        });
}

Расположение кнопок

Используйте свойство DialogChoice<T>.Dock, чтобы принудительно разместить определённые кнопки первыми (задайте свойству Dock значение DockType.Left) или последними (задайте свойству Dock значение DockType.Right) среди всех кнопок. По умолчанию свойство Dock имеет значение 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 },
        });
}

Обработка закрытия диалога без выбора

Когда пользователь закрывает диалог кнопкой закрытия ('x'), метод Show возвращает default(T):

  • Для ссылочных типов (string и т. д.) результат равен null.
  • Для значимых типов (bool, перечисления и т. д.) результатом является значение типа по умолчанию (false для логического типа, 0 для перечисления и т. д.)

Если в диалоге есть кнопка отмены (кнопка выбора, у которой свойство IsCancel равно true), нажатие на неё или клавиши Escape закрывает диалог с результатом этой кнопки. Кнопка закрытия ('x') по-прежнему закрывает диалог с результатом default(T).

Пример — обработка закрытия диалога

В этом примере создаётся диалог с кнопкой отмены, связанной с результатом диалога null. В этом случае достаточно проверить результат на null, чтобы обработать все сценарии закрытия диалога:

  • Нажата кнопка отмены.
  • Нажата кнопка x в заголовке.
  • Нажата клавиша Escape.
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.
}



* Эта страница переведена с использованием технологий машинного перевода.