Отображение подтверждения при закрытии окна реактивным способом

у меня есть наблюдаемый, который я использую для отображения диалога подтверждения, примерно подписи:

IObservable<DialogResult> ShowDialog(string title, string message);

это показывает пользователю диалоговое окно с комбинацией кнопок да / нет.

в моем главном окне, я получаю доступ к закрытия мероприятия, как например:

this.Events().Closing.[Do something here]

Я хочу иметь возможность:

  1. показать диалог подтверждения при закрытии наблюдаемых пожаров
  2. установить CancelEventArgs.Cancel свойство true, если пользователь нажимает кнопку " нет" кнопка.

Я пробовал прямо подписываться:

this.Events().Closing.Subscribe(e =>
{
    var res = Dialogs.ShowDialog("Close?", "Really Close?").Wait();
    e.Cancel = res == DialogResult.Ok;
});

но это висит из-за Wait() вызов, я также пробовал асинхронный вариант:

this.Events().Closing.Subscribe(async e =>
{
    var res = await Dialogs.ShowDialog("Close?", "Really Close?");
    e.Cancel = res == DialogResult.Ok;
});

что нехорошо, потому что он просто сразу возвращается.

что я действительно хочу сделать, это что-то вроде:

this.Events().Closing.ThenDo(_ => Dialogs.ShowDialog("Close?", "Really Close?"))
    .Subscribe((cancelEventArgs, dialogResult) =>
    {
        cancelEventArgs.Cancel == dialogResult == DialogResult.Ok;
    });

но это не существует, я знаю, что ключ здесь в том, как я комбината двух наблюдаемых, но я понятия не имею, как это сделать, и документация немного освещает практические примеры.

2 ответов


вы нужно чтобы заблокировать обработчик событий закрытия, следовательно, асинхронная (или RX-манипуляция)не поможет вам здесь.

но вам также нужно заблокировать его таким образом, чтобы события пользовательского интерфейса все еще обрабатывались, поэтому пользовательский интерфейс не замерзает.

самое распространенное решение-это использовать окно.ShowDialog вместо Show, и этот код работает:

        this.Events().Closing.Subscribe(e =>
        {
            var ret = new Window().ShowDialog();
            e.Cancel = true;
        });

но использование этого в вашем методе ShowDialog Rx сделает его блок вызова подписки, и это маловероятно что вы хотите (для других случаев, в этом случае это is то, что вам нужно).

В качестве альтернативы вы можете запустить внутренний цикл диспетчера, например:

        this.Events().Closing.Subscribe(e =>
        {
            var dialog = Dialogs.ShowDialog("Close?", "Really Close?");
            var dispatcherFrame = new DispatcherFrame();
            dialog.Take(1).Subscribe(res => {
                e.Cancel = res == DialogResult.Ok;
                dispatcherFrame.Continue = false;
            });
            // this will process UI events till the above fires
            Dispatcher.PushFrame(dispatcherFrame);
        });

это будет работать, только если один и тот же диспетчер используется обоими окнами.

EDIT:

кроме того, вы можете избежать блокировки, всегда отменяя закрытие и закрывая форму позже, что, возможно, больше Rx-way, т. е.:

        var forceClose = default(bool);

        this.Events().Closing
            .Where(_ => !forceClose)
            .Do(e => e.Cancel = true)
            .SelectMany(_ => Dialogs.ShowDialog("Close?", "Really Close?"))
            .Where(res => res == DialogResult.Ok)
            .Do(_ => forceClose = true)
            .Subscribe(_ => this.Close());

лучший способ объединить наблюдаемые потоки-CombineLatest. Это будет срабатывать, когда любой из них обновляется с последним значением в каждом потоке.

канал 9 видео на Combine Latest