2 回答

TA貢獻(xiàn)1847條經(jīng)驗(yàn) 獲得超11個(gè)贊
我認(rèn)為您的答案可以簡化。
var itemChangedObs = this.WhenAnyValue(x => x.MyObject.Active);
var isBusyObs = this.WhenAnyValue(x => x.IsBusy);
var listItemChangedObs = this.WhenAnyObservable(x => x.MyObectsList.ItemChanged).Where(x => x.PropertyName == "Active").Select(_ => MyObjectsList.Any(x => x.Active)).StartsWith(false)
var canRunCommand = itemChangedObs.CombineLatest(listItemChangedObs, isBusyObs, (itemActive, listItemActive, isBusy) => (itemActive || listItemActive) && !isBusy);
這個(gè)版本本質(zhì)上使用了一個(gè) CombineLatest ,它在將兩個(gè) observable 組合在一起后采用您想要的值的 Lambda。
在兩個(gè) Observables 都發(fā)出一個(gè)值之前,CombineLatest() 不會(huì)產(chǎn)生值,因此為什么 listItemChanged 在前面有一個(gè) StartsWith(false)。
默認(rèn)情況下,WhenAnyValue() 將始終發(fā)出值的 default(T) 作為其初始值,因此您不需要這些語句的 StartsWith。

TA貢獻(xiàn)1824條經(jīng)驗(yàn) 獲得超6個(gè)贊
這是另一種替代方法,它避免了對(duì)復(fù)雜度為 O(n) 的 MyObjectList.Any() 的需要。此解決方案涉及更多,但具有提高效率的潛力。除了計(jì)算listItemChangedObs observable的方式外,它與 Glenn 的CombineLatest方法相同。此版本保持列表中活動(dòng)對(duì)象數(shù)的運(yùn)行總數(shù)。因此,每次觸發(fā) ItemChanged 時(shí),它只需執(zhí)行 +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);
}
這是我運(yùn)行代碼時(shí)通過的單元測試。它基本上檢查 ReactiveCommand 的CanExecute更改狀態(tài)的次數(shù),以及它是真還是假,每次變量之一發(fā)生變化時(shí)。
[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]);
}
- 2 回答
- 0 關(guān)注
- 483 瀏覽
添加回答
舉報(bào)