来自Observable.Timer的InvokeCommand导致跨线程问题

时间:2017-06-02 16:51:08

标签: wpf reactiveui

我有一个ReactiveCommand刷新数据并绑定到XAML中的Button。功能正常,但我也想在计时器上执行命令。

我有以下代码 - 我的VM中的ctor调用了SetupAutoRefresh,但是当Observable触发时,我收到一条消息的异常:"调用线程因为不同而无法访问此对象线程拥有它。"

VM:

private void SetupAutoRefresh() {
    Observable.Timer(TimeSpan.FromSeconds(5))
            .Select(_ => Unit.Default)
            .ObserveOn(RxApp.MainThreadScheduler)
            .InvokeCommand(RefreshData);

    RefreshData = ReactiveCommand.CreateFromTask(Refresh);
}

private async Task Refresh()
{
    var updatedData = await _repository.GetAll();
    Data.Merge(updatedData);
}

private ReactiveCommand<Unit, Unit> _refreshData;
public ReactiveCommand<Unit, Unit> RefreshData
{
    get { return _refreshData; }
    set { this.RaiseAndSetIfChanged(ref _refreshData, value); }
}

private IReactiveList<Model> _data;
public IReactiveList<Model> Data
{
    get { return _data; }
    set { this.RaiseAndSetIfChanged(ref _data, value); }
}

XAML:

<Button Grid.Column="2"
        Command="{Binding RefreshData}"
        Style="{StaticResource ToolbarButtonTheme}"
        Content="{StaticResource RefreshToolbarIcon}"
        ToolTip="Refresh Search"/>

Debug输出提供了这个堆栈跟踪:

  System.Windows.Threading.Dispatcher.VerifyAccess()上的

    在System.Windows.DependencyObject.GetValue(DependencyProperty dp)     在System.Windows.Controls.Primitives.ButtonBase.get_Command()     在System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()     在System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object&gt; sender,EventArgs e)     在    System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender,EventArgs e)      在C:\ projects \ reactiveui \ src \ ReactiveUI \ ReactiveCommand.cs中的ReactiveUI.ReactiveCommand.OnCanExecuteChanged():第628行

我尝试过尝试在RxApp.MainThreadScheduler上安排此操作的许多不同变体,但没有任何乐趣 - ObserveOn,SubscribeOn,设置输出调度程序......无论如何我都没有希望。

感觉我在这里错过了一些明显的东西,但整个下午一直在我的头撞墙。当然这种情况在RxUI中是可能的吗?

2 个答案:

答案 0 :(得分:0)

Refresh方法在后台线程上运行;你不能修改那个方法中的数据绑定属性。

试试这个:

private void SetupAutoRefresh() {
    Observable.Timer(TimeSpan.FromSeconds(5))
            .Select(_ => Unit.Default)
            // remove ObserveOn here; the Command will run on the background
            .InvokeCommand(RefreshData);

    RefreshData = ReactiveCommand.CreateFromTask(Refresh);
    // RefreshData.Subscribe is guaranteed to run on the UI thread
    RefreshData.Subscribe(listOfModels => Data.Merge(listOfModels))
}

private async Task Refresh()
{
    // all this method does is deliver a list of models
    return await _repository.GetAll();
}

// return IEnumerable<Model> from the command
public ReactiveCommand<Unit, IEnumerable<Model>> RefreshData

现在,您的ReactiveCommand只是抓取新数据,并在Subscribe内的UI线程上将其返回给您:)

答案 1 :(得分:0)

找出问题 - 看起来需要在UI线程上创建Observable。我错过了原帖,但是SetupAutoRefresh方法是从另一个异步方法中调用的,该方法在之前的等待中切换了上下文。