Отображение подтверждения при закрытии окна реактивным способом
у меня есть наблюдаемый, который я использую для отображения диалога подтверждения, примерно подписи:
IObservable<DialogResult> ShowDialog(string title, string message);
это показывает пользователю диалоговое окно с комбинацией кнопок да / нет.
в моем главном окне, я получаю доступ к закрытия мероприятия, как например:
this.Events().Closing.[Do something here]
Я хочу иметь возможность:
- показать диалог подтверждения при закрытии наблюдаемых пожаров
- установить
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. Это будет срабатывать, когда любой из них обновляется с последним значением в каждом потоке.