2017-06-02 8 views
0

データを更新し、XAMLのボタンにバインドされているReactiveCommandがあります。機能は正常に機能しますが、私はタイマーでコマンドを実行したいと思います。Observable.TimerのInvokeCommandがスレッド間の問題を引き起こします

私の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:システムでSystem.Windows.Threading.Dispatcher.VerifyAccess() で

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

デバッグ出力は、このスタックトレースを提供します.Windows.DependencyObject.GetValue(DependencyProperty dp) System.Windows.Controls.Primitives.ButtonBase.getCommand()でSystem.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()で にSystem.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object> sender、EventArgs e)で に設定します。 System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(オブジェクト送信者、EventArgsの電子)ReactiveUI.ReactiveCommand.OnCanExecuteChanged()C中で で :\プロジェクト\ reactiveui \ SRC \ reactiveUI \ ReactiveCommand.cs:ライン628

私はRxApp.MainThreadSchedulerでこれをスケジュールしようとしましたが、出力スケジューラを設定するどんな喜びもなしに - ObserveOn、SubscribeOnを試してみました...とにかく私はとにかく大きな希望を持っていました。

私はここで何かが分からないように感じますが、午後は煉瓦の壁に頭を打っています。確かにこのシナリオはRxUIで可能ですか?

答えて

0

- 観察可能なように見えますUIスレッドで作成する必要がありました。私は元の投稿からそれを逃したが、SetupAutoRefreshメソッドは、別の非同期メソッドから呼び出されていました。これは、前の待機中にコンテキストを切り替えました。

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スレッド上であなたにそれを返す:)問題を考え出した

+0

ありがとう、私もこれを試していましたが、基本的な問題に対処していないため、機能しません。これは不平を言っているデータバインドされたプロパティではありませんが、コマンドはボタンにバインドされています。具体的には、コマンドの実行中にCanExecuteが変更された場合(おそらくfalse)。私の元の投稿のstacktraceを見てください。 – Mike

+0

さて、元のコードをコピーし、依存関係([MainViewModel.cs](https://gist.github.com/domyd/73895b166b160b9d09d5902e24bd97ef)、[MainWindow.xaml](https:// gist)を作成しました。 github.com/domyd/350e8aef743d348e183b5c496391e220))。これは私にとって完璧に機能します。私が変更したのは 'SetupAutoRefresh'の' Observable'と 'ReactiveCommand'作成順序を入れ替えただけでした。そうしないとクラッシュしました。また、 'Merge'を' Add'に置き換えました。多分それが助けますか? – domyd

関連する問題