【问题标题】:ReactiveUI - Using a Scheduler in a Interaction handlerReactiveUI - 在交互处理程序中使用调度程序
【发布时间】:2016-12-13 17:38:30
【问题描述】:

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

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

问题是异常是在第三方库的线程内引发的。 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;
    });
});

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

【问题讨论】:

    标签: system.reactive reactiveui


    【解决方案1】:

    如果您检查崩溃的详细信息,我怀疑您会发现它抱怨没有处理交互。原因是您注册的处理程序是同步的。当然,它启动了异步工作,但是没有给 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)));
    

    Observable.Start 调用有点让我们进入正确的线程,如果我是你,我会研究如何清理它。具体来说,我会考虑在正确的线程上发起交互。也就是说,无论调用Handle(可能是您的VM)都应该在UI 线程上执行此操作。在正确的线程上执行命令是您的责任,Handle 也是如此。

    【讨论】:

    • 和你一样,我也喜欢 Rx 语法来表达异步,但我错过了 Unit observable 技巧。从来没有想过。您需要在 Do 语句之后添加 .Select(_ => Unit.Default) ,否则仍将使用 Action 重载。它仍然崩溃,但现在我可以看到异常:“无效的窗口句柄。必须从具有 CoreWindow 的线程调用此 API,否则必须显式设置窗口。”在
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多