我是ReactiveUI的新手,我试图检测ReactiveObject图中的任何属性变化。
下面是一个简单的例子,Type1, Type2和Type3都是reactiveobject。
在顶级Type1实例中,当发生以下任何更改时,我将调用CheckIfHasChanges方法:
-
从Type2的Type3集合中添加/更新或删除一个新的Type3项
-
Type2的Type3集合中已有的Type3项的Name属性发生变化
public class Type1 : ReactiveObject { private ObservableCollection<Type2> _type2s = new ObservableCollection<Type2>(); public Type1() { this.WhenAnyValue(x => x.Type2s) .Subscribe(_ => CheckIfHasChanges()); } private void CheckIfHasChanges() { // Do something on change } public ObservableCollection<Type2> Type2s { get => _type2s; set => this.RaiseAndSetIfChanged(ref _type2s, value, nameof(Type2s)); } } public class Type2:ReactiveObject { private ObservableCollection<Type3> _type3s = new ObservableCollection<Type3>(); public ObservableCollection<Type3> Type3s { get => _type3s; set => this.RaiseAndSetIfChanged(ref _type3s, value, nameof(Type3s)); } } public class Type3 : ReactiveObject { private string _name; public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value, nameof(Name)); } } class Program { static void Main(string[] args) { var t1 = new Type1(); // Initial call into Type1's CheckChanges happens here var t2 = new Type2(); var t3 = new Type3 { Name = "First" }; t2.Type3s.Add(t3); t1.Type2s.Add(t2); // Would also like see call to Type1's CheckChanges method t3.Name = "Second"; // Would also like see call to Type1's CheckChanges method } }
这个问题的答案有点棘手,因为有很多移动的目标,不仅需要观察集合的变化,还需要监视子集合和其中的属性。
对于这样的任务,你可以使用动态数据(我是作者),它与RxUI捆绑在一起。动态数据是基于Rx的,但是有丰富的扩展,可以对整个集合进行观察和操作。
为了分解它,为了不使解决方案混乱,我将只发布代码的摘录:
首先,添加名称空间:using DynamicData;
using DynamicData.Binding;
Type2添加以下属性(我希望注释解释它):
public IObservable<Unit> HasAnythingChanged => this.WhenValueChanged(t => t.Type3s)
.SelectMany(t =>
{
//account for the dreaded null
if (t == null) return Observable.Return(Unit.Default);
//watch the collection changes
var collectionChanges = t.ToObservableChangeSet().Select(_ => Unit.Default);
//watch the property changes - use .WhenValueChanged(t=>t.Name, false) if you do not want the initial value and only want subsequent changes
var propertyChanged = t.ToObservableChangeSet().WhenValueChanged(t=>t.Name).Select(_ => Unit.Default);
//Combine the above and use StartWith(Unit.Default) so it always fires after Type3 is set
return collectionChanges.Merge(propertyChanged).StartWith(Unit.Default);
});
类型1我们做了类似的事情,但我们利用了Type2.HasAnythingChanged property
public IObservable<Unit> HasAnythingChanged => this.WhenValueChanged(t => t.Type2s)
.SelectMany(t =>
{
//account for the dreaded null
if (t == null) return Observable.Return(Unit.Default);
//watch Type2 collection changes
var collectionChanges = t.ToObservableChangeSet().Select(_ => Unit.Default);
//watch for changes from Type2.HasAnythingChanged
var propertyChanged = t.ToObservableChangeSet().MergeMany(x=>x.HasAnythingChanged).Select(_ => Unit.Default);
return collectionChanges.Merge(propertyChanged).StartWith(Unit.Default);
});
最后,您需要观察Type1的变化:
var hasAnythingChanged = t1.HasAnythingChanged
.Subscribe(_=> Console.WriteLine("There has been a change"));
我没有测试代码,但它应该工作。我建议你写一些单元测试,从检查T2是否。"HasAnythingChanged"是指孩子的变化。如果你让它工作,那么你可以扩展你的测试类型1。
这里可能有很多新概念,但关键的是:
ToObservableChangeSet()
将可观察集合改为变化的可观察对象。这将监视可观察对象集合的添加、替换和删除。
MergeMany
,它合并了底层集合中每个项目的可观察对象。当项被添加或删除时,可观察对象会相应地被连接/断开连接。
我在这里添加了一个要点供参考https://gist.github.com/RolandPheasant/81dcc207bf95f6c4cf83f0ecd48ed740