以反应方式在窗口关闭时显示确认

时间:2015-05-20 17:56:12

标签: c# wpf mvvm reactiveui

我有一个可观察的,用于显示确认对话框,大致是签名:

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

这向用户显示了对话框,带有是/否按钮组合。

在我的主窗口中,我正在访问结束事件:

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

我希望能够:

  1. 关闭observable触发时显示确认对话框
  2. 如果用户点击“否”按钮,则将CancelEventArgs.Cancel属性设置为true。
  3. 我尝试过直接订阅:

    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 个答案:

答案 0 :(得分:2)

需要来阻止关闭事件处理程序,因此异步(或Rx操作)在这里不会对你有所帮助。

但您还需要以仍处理UI事件的方式阻止它,因此UI不会冻结。

最常见的解决方案是使用Window.ShowDialog而不是Show,并且此代码有效:

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

但是在你的ShowDialog Rx方法中使用它会使其订阅调用块,并且这不太可能你想要的(对于其他情况,在这种情况下,它 你需要的)。

或者你可以运行一个内部调度程序循环,如下所示:

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

只有在两个窗口都使用相同的Dispatcher时才会起作用。

编辑:

或者,您可以通过总是取消关闭来避免阻止,并在以后自己关闭表单,这可能更多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());

答案 1 :(得分:-1)

组合到可观察流的最佳方法是CombineLatest。 当使用每个流中的最后一个值更新任一个时,它将触发。

Channel 9 video on Combine Latest