我在我正在处理的应用程序中使用ReactiveUI 7.4。其中一个视图是主/详细视图。选择新的主记录时,我想从数据库加载详细信息项。这可以使用WhenAnyValue
observable按预期工作,在下面的示例代码中设置ObservableAsPropertyHelper
的值。
现在,我有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
另一个可能的答案:
我还发现我可以转换&#34;加载相关的项目&#34;将逻辑转换为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 :(得分: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是否会起作用。但是如果它没有,你可以在用ToProperty(...)转换之前将Observable创建为一个单独的项目。