ReactiveUI - 在交互处理程序中使用调度程序

Ant*_*ada 4 system.reactive reactiveui

我想在发生错误时显示带有两个按钮的警报对话框。据我所知,这是使用交互属性来做到这一点的方法:

this.ViewModel.ConnectionError.RegisterHandler(interaction =>
{
    var retry = await this.DisplayAlert("Connection failed", "Do you want to retry?", "RETRY", "ABORT");
    if (retry)
        interaction.SetOutput(DevicesViewModel.ErrorRecoveryOption.Retry);
    else
        interaction.SetOutput(DevicesViewModel.ErrorRecoveryOption.Abort);
});
Run Code Online (Sandbox Code Playgroud)

问题在于异常是在第三方库的线程内引发的。DisplayAlert 必须在主线程中调用。我尝试了以下方法:

this.ViewModel.ConnectionError.RegisterHandler(interaction =>
{
    RxApp.MainThreadScheduler.ScheduleAsync(interaction, async (scheduler, i, cancelationToken) =>
    {
        this.Log().Debug("ScheduleAsync");
        var retry = await this.DisplayAlert("Connection failed", "Do you want to retry?", "RETRY", "ABORT");
        if (retry)
            i.SetOutput(DevicesViewModel.ErrorRecoveryOption.Retry);
        else
            i.SetOutput(DevicesViewModel.ErrorRecoveryOption.Abort);

        return Disposable.Empty;
    });
});
Run Code Online (Sandbox Code Playgroud)

我可以在控制台中看到日志消息,但对话框不显示,并且应用程序在 ReactiveUI.dll 内崩溃。我究竟做错了什么?

Ken*_*art 5

如果你检查崩溃的细节,我怀疑你会发现它抱怨没有任何东西处理交互。原因是您注册的处理程序是同步的。当然,它会启动异步工作,但是 ReactiveUI 没有提供任何等待该工作完成的方法。

您可以通过查看RegisterHandler您的调用解决的过载来验证这一点。你会发现它是RegisterHandler(Action<InteractionContext<TInput, TOutput>>). 换句话说,“注册一个处理程序,该处理程序获取上下文并同步处理交互。

您想要做的是调用异步RegisterHandler方法之一:

  • RegisterHandler(Func<InteractionContext<TInput, TOutput>, Task>)
  • RegisterHandler(Func<InteractionContext<TInput, TOutput>, IObservable<Unit>>)

有多种方法可以为此编写逻辑。我更喜欢 Rx 作为表达异步的一种方式,所以我会这样写:

this
    .ViewModel
    .ConnectionError
    .RegisterHandler(
        context =>
            Observable
                .Start(
                    () => Unit.Default,
                    RxApp.MainThreadScheduler)
                .SelectMany(_ => this.DisplayAlert("Connection failed", "Do you want to retry?", "RETRY", "ABORT"))
                .Do(result => context.SetOutput(result ? ErrorRecoveryOption.Retry : ErrorRecoveryOption.Abort)));
Run Code Online (Sandbox Code Playgroud)

为了让我们进入正确的线程,这个Observable.Start调用有点麻烦,如果我是你,我会考虑清理它的方法。具体来说,我会考虑在正确的线程上发起交互。也就是说,任何调用Handle(可能是您的虚拟机)都应该在 UI 线程上执行。尽管您有责任在正确的线程上执行命令,Handle但这是相同的。