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

Cli*_*int 6 c# wpf mvvm reactiveui

我有一个observable,我用它来显示一个确认对话框,大致是签名:

IObservable<DialogResult> ShowDialog(string title, string message);
Run Code Online (Sandbox Code Playgroud)

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

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

this.Events().Closing.[Do something here]
Run Code Online (Sandbox Code Playgroud)

我希望能够:

  1. 触发关闭observable时显示确认对话框
  2. CancelEventArgs.Cancel如果用户单击"否"按钮,则将该属性设置为true.

我试过直接订阅:

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

但是因为这个Wait()电话而挂起,我也尝试过异步变种:

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

哪个不好,因为它马上就会回来.

我真正想要做的是:

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

但这不存在,我知道这里的关键在于我如何组合这两个可观察量,但我不知道如何这样做,而且文档对实际例子有点了解.

Glu*_*uck 3

需要阻止 Closing 事件处理程序,因此异步(或 Rx 操作)在这里对您没有多大帮助。

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

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

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

但是在 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);
        });
Run Code Online (Sandbox Code Playgroud)

仅当两个窗口使用相同的调度程序时,这才有效。

编辑:

或者,您可以通过始终取消关闭并稍后自己关闭表单来避免阻塞,这可能更像是 Rx 方式,即:

        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());
Run Code Online (Sandbox Code Playgroud)