私が作業しているアプリケーションで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);
});
ありがとうございます!あなたの答えは正しい方向で私に指摘しました。 –