2017-11-08 14 views
0

私が作業しているアプリケーションでReactiveUI 7.4を使用しています。ビューの1つはマスター/詳細ビューです。新しいマスターレコードが選択されると、データベースから詳細項目をロードします。これは、WhenAnyValueを観測して、私のサンプルコードでObservableAsPropertyHelperの値を設定して、期待通りに機能します。WhenAnyValueがRaisePropertyChangedによってトリガされない

今、データを保存するためにReactiveCommandが使用されています。このコマンドは、現在選択されているマスターアイテムに関連する子アイテムを処理および更新するストアドプロシージャを呼び出します。関連項目はデータベース側で変更される可能性があるので、私が既にWhenAnyValueで設定したロジックを使用して、関連項目を再ロードするようにトリガーしたいと思います。

以下は、私がしようとしていることを再現する非常に単純化された例です。 保存の最後にRaisePropertyChangedを呼び出そうとしましたが、それは観測可能なWhenAnyValueをトリガーしません。選択したアイテムをnullに設定して元の値に戻すことができますが、ここでは間違っています。

この状況を処理するには、より良い方法がありますか?WhenAnyValue/ObservableAsPropertyHelperをトリガーする方法はありますか?

public class SampleViewModel : ReactiveObject 
{ 
    public SampleViewModel() 
    { 
     // log any changes (we can see the RaisePropertyChanged triggers this observable, but not the one above). 
     this.Changed.Skip(1).Subscribe(x => Console.WriteLine($"\tChanged = {x.PropertyName}")); 

     // when the selected item changes, we want to load related items from the database 
     _relatedItems = this.WhenAnyValue(x => x.SelectedItem) 
      .Where(row => row != null) 
      .Select(row => 
      { 
       // get related data from the cache or database... 
       Console.WriteLine($"\tGetting Related Items for {row["Value"]}"); 
       return new DataTable(); 
      }) 
      .ToProperty(this, x => x.RelatedItems); 

     Save = ReactiveCommand.Create(() => 
     { 
      // Save the selected item. Stored procedure does some extra processing of related items 

      // Now, we want to trigger a reload of the related items. 
      this.RaisePropertyChanged(nameof(SelectedItem)); 

      /* NOTE: The following works, but feels wrong. 
      var oldSelection = SelectedItem; 
      SelectedItem = null; 
      SelectedItem = oldSelection; 
      */ 
     }); 
    } 

    public ReactiveCommand Save { get; } 

    private readonly ObservableAsPropertyHelper<DataTable> _relatedItems; 
    public DataTable RelatedItems => _relatedItems.Value; 

    private DataRow _selectedItem; 
    public DataRow SelectedItem 
    { 
     get { return _selectedItem; } 
     set { this.RaiseAndSetIfChanged(ref _selectedItem, value); } 
    } 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     using (var table = new DataTable()) 
     { 
      table.Columns.Add(new DataColumn("Value", typeof(string))); 
      table.Rows.Add(new[] { "First Row" }); 
      table.Rows.Add(new[] { "Second Row" }); 

      var instance = new SampleViewModel(); 
      Console.WriteLine("Selecting First Row"); 
      instance.SelectedItem = table.Rows[0]; 

      Console.WriteLine(); 
      Console.WriteLine("Saving Data"); 
      Observable.Start(() => { }).InvokeCommand(instance.Save); 

      Console.WriteLine(); 
      Console.WriteLine("Selecting Second Row"); 
      instance.SelectedItem = table.Rows[1]; 

      Console.WriteLine(); 
     } 
    } 
} 

出力

Selecting First Row 
    Changed = SelectedItem 
    Getting Related Items for First Row 
    Changed = RelatedItems 

Saving Data 
    Changed = SelectedItem 
    // should be getting related items here... 

Selecting Second Row 
    Changed = SelectedItem 
    Getting Related Items for Second Row 
    Changed = RelatedItems 

編集(作業コード)

Lee McPherson's Answerは正しい方向に私を指摘しました。コンストラクタを次のように更新されました:

public SampleViewModel() 
{ 
    // log any changes 
    this.Changed.Skip(1).Subscribe(x => Console.WriteLine($"\tChanged = {x.PropertyName}")); 

    Save = ReactiveCommand.Create(() => 
    { 
     // Save the selected item. Stored procedure does some extra processing of related items 
    }); 

    // when the save command finishes execution, get the currently selected "master" record 
    var saveCommandDone = Save.IsExecuting 
     .Where(executing => !executing).Skip(1) 
     .Do(_ => Console.WriteLine("\tCommand finished execution")) 
     .Select(_ => SelectedItem); 

    // when the master record changes, or data is saved, reload the related items 
    _relatedItems = Observable.Merge(saveCommandDone, this.WhenAnyValue(x => x.SelectedItem)) 
     .Where(row => row != null) 
     .Select(row => 
     { 
      // get related data from the cache or database... 
      Console.WriteLine($"\tGetting Related Items for {row["Value"]}"); 
      return new DataTable(); 
     }) 
     .ToProperty(this, x => x.RelatedItems); 
} 
Selecting First Row 
    Changed = SelectedItem 
    Getting Related Items for First Row 
    Changed = RelatedItems 

Saving Data 
    Command finished execution 
    Getting Related Items for First Row 
    Changed = RelatedItems 

Selecting Second Row 
    Changed = SelectedItem 
    Getting Related Items for Second Row 
    Changed = RelatedItems 

別の可能な答え: 私も、私はReactiveCommandに「負荷に関する項目」の論理を変換し、同じことを達成するためにInvokeCommandを使用することができることを発見しました。

var loadRelatedData = ReactiveCommand.Create<DataRow, DataTable>(row => new DataTable()); 
_relatedItems = loadRelatedData.ToProperty(this, x => x.RelatedItems); 

// when the selected item changes, trigger the command used to load related data 
this.WhenAnyValue(x => x.SelectedItem) 
    .Where(row => row != null) 
    .InvokeCommand(loadRelatedData); 

Save = ReactiveCommand.Create(() => 
{ 
    // Save the data and trigger a reload of the related data. 
    Observable.Return(SelectedItem).InvokeCommand(loadRelatedData); 
}); 

答えて

0

あなたの保存ReactiveCommandはIsExecuting IObservableを持っています。 Observable.Mergeを試して、あなたが作成したオブザーバブルとWhenAnyValueをコマンドの呼び出しと組み合わせることができます。

Observable.Merge(this.Save.IsExecuting, _relatedItems).Subscribe(x=>{ 
    //do stuff 
}); 

ここでは、関連の答えです: How do I merge several observables using WhenAny(...) in ReactiveUI?

がObservableAsPropertyHelperが動作するかどうか今、私はそれについて考えることを、私は知りません。しかし、そうでなければObservableをToProperty(...)で変換する前に別の項目として作成することができます。

+1

ありがとうございます!あなたの答えは正しい方向で私に指摘しました。 –

関連する問題