慕妹3242003
这是另一种替代方法,它避免了对复杂度为 O(n) 的 MyObjectList.Any() 的需要。此解决方案涉及更多,但具有提高效率的潜力。除了计算listItemChangedObs observable的方式外,它与 Glenn 的CombineLatest方法相同。此版本保持列表中活动对象数的运行总数。因此,每次触发 ItemChanged 时,它只需执行 +1 或 -1 操作。然后它只检查它是否大于 0。public MyViewModel(){ var itemChangedObs = this.WhenAnyValue(x => x.MyObject.Active); var isBusyObs = this.WhenAnyValue(x => x.IsBusy); // Recalculate the # of active objects each time ObjectList is reassigned. var activeListItemCountInitializedObs = this .WhenAnyValue(x => x.ObjectList) .Select( list => { // Return 0 if ObjectList is null. return list == null ? Observable.Return(0) : list .ToObservable() // Otherwise, increment by 1 for each active object. .Select(x => x.Active ? 1 : 0) // We use Aggregate, which is a single value sequence, because // we're only interested in the final result. .Aggregate((acc, current) => acc + current); }) // We no longer need the inner observable from the last time active item count // was initialized. So unsubscribe from that one and subscribe to this most recent one. .Switch(); var activeListItemCountChangedObs = this .WhenAnyObservable(x => x.ObjectList.ItemChanged) .Where(x => x.PropertyName == "Active") // Increment or decrement the number of active objects in the list. .Select(x => x.Sender.Active ? 1 : -1); // An IObservable<bool> that signals if *any* of objects in the list are active. var anyListItemsActiveObs = activeListItemCountInitializedObs .Select( // Use the initialized count as the starting value for the Scan accumulator. initialActiveCount => { return activeListItemCountChangedObs .Scan(initialActiveCount, (acc, current) => acc + current) // Return true if one or more items are active. .Select(x => x > 0) .StartWith(initialActiveCount > 0); }) // ObjectList was completely reassigned, so the previous Scan accumulator is // no longer valid. So we "reset" it by "switching" to the new one. .Switch(); var canRunCommand = itemChangedObs .CombineLatest( anyListItemsActiveObs, isBusyObs, (itemActive, listItemActive, isBusy) => (itemActive || listItemActive) && !isBusy); Save = ReactiveCommand.CreateFromObservable(() => Observable.Return(Unit.Default), canRunCommand);}这是我运行代码时通过的单元测试。它基本上检查 ReactiveCommand 的CanExecute更改状态的次数,以及它是真还是假,每次变量之一发生变化时。[Fact]public void TestMethod1(){ var objectList = new ReactiveList<IMyObject>( initialContents: new[] { new MyObject(), new MyObject() }, resetChangeThreshold: 0.3, scheduler: ImmediateScheduler.Instance); objectList.ChangeTrackingEnabled = true; IMyViewModel myViewModel = new MyViewModel { ObjectList = objectList, MyObject = new MyObject() }; var canExecute = myViewModel.Save .CanExecute .CreateCollection(scheduler: ImmediateScheduler.Instance); Assert.Equal(1, canExecute.Count); Assert.False(canExecute[0]); myViewModel.ObjectList[0].Active = true; Assert.Equal(2, canExecute.Count); Assert.True(canExecute[1]); myViewModel.MyObject.Active = true; Assert.Equal(2, canExecute.Count); myViewModel.IsBusy = true; Assert.Equal(3, canExecute.Count); Assert.False(canExecute[2]); myViewModel.IsBusy = false; Assert.Equal(4, canExecute.Count); Assert.True(canExecute[3]); myViewModel.MyObject.Active = false; Assert.Equal(4, canExecute.Count); var object1 = new MyObject { Active = true }; var object2 = new MyObject { Active = true }; myViewModel.ObjectList = new ReactiveList<IMyObject>( initialContents: new[] { object1, object2 }, resetChangeThreshold: 0.3, scheduler: ImmediateScheduler.Instance); Assert.Equal(4, canExecute.Count); object1 = new MyObject { Active = false }; object2 = new MyObject { Active = false }; myViewModel.ObjectList = new ReactiveList<IMyObject>( initialContents: new[] { object1, object2 }, resetChangeThreshold: 0.3, scheduler: ImmediateScheduler.Instance); Assert.Equal(5, canExecute.Count); Assert.False(canExecute[4]);}