有没有办法为反应性扩展可观察量指定初始值?

时间:2013-11-01 05:37:33

标签: c# wpf reactiveui

在activeui和reactive扩展中,您可以将多个observable组合成一个。使用像CombineLatest或Zip这样的函数,您可以使得结果可观察使用一个函数来根据组合的可观察量的值计算结果。虽然CombineLatest为您提供了一个可观察的,它可以在每次组合的可观察者触发时向您发出通知,但只有当所有这些可观察者至少发射一次时它才会开始工作。我希望它从一开始就发出通知 - 给组合的observable一个初始值,或者其他东西。当然,我可以以某种方式使那些可观察的火来设定初始值。但有时候并不容易,或者它使代码看起来很难看。这是一个例子: 我正在使用reactiveui制作一个wpf应用程序。在我的一个视图模型中,我试图通过组合observable来创建属性。所以,我有一个具有NumberToSum属性的对象集合(ReactiveList),我需要一个属性来显示该属性值的总和。该集合中的对象也是视图模型,用户可以更改其NumberToSum属性。求和的对象数由其他属性决定。这就是我尝试这样做的方式:

private readonly ReactiveList<Element> _collection = new ReactiveList<Element>();
private readonly ObservableAsPropertyHelper<int> _sum;
private int _elementsToTake = 0;

public int ElementsToTake
{
    get { return _elementsToTake; }
    set { this.RaiseAndSetIfChanged(ref _elementsToTake, value) }
}

public int Sum
{
    get { return _sum.Value; }
}

public MyViewModel
{
    var elementsToTakeObservable = this
        .WhenAny(x => x.ElementsToTake, x => x.Value);
    _collection.ChangeTrackingEnabled = true;
    //here I'm adding some objects to _collection
    var sumObservable = _collection.ItemChanged
        .CombineLatest(elementsToTakeObservable,
            (_, c) => _collection.Take(c)
                .Aggregate(0, (i, x) => i + x.NumberToSum))).
        .ToProperty(this, x => x.Sum, out _sum, 0);
}

因此,我使用ReactiveList.ItemChanged来跟踪_collection项目中的更改。每次发生变化时都会重新计算Sum。这里的问题是可观察量的初始值组合成sumObservable。我可以通过为ElementsToTake属性赋值来轻松设置elementsToTakeObservable的初始值,这将引发属性更改事件和内容。但是_collection.ItemChanged可观察并不容易。我需要在_collection raise propertychanged事件中至少创建一个我的对象,但这看起来很糟糕。 设置这些初始值是否可行?或者也许我这样做完全错了?

1 个答案:

答案 0 :(得分:7)

StartWith会解决您的问题。此外,我不确定您是否需要ItemChanged,它会在列表中的项更改时发出信号,而不是在项目被添加或删除时发出信号。在这里,您可能还想听Changed

虽然尝试以这种方式保持“跑步总数”很诱人,但实际上很难做到这一点,因为重置和移动。一种更简单的方法是滥用CPU非常快的事实,并在每次更改时重新计算它:

_collection.ChangeTrackingEnabled = true;

Observable.Merge(
         _collection.Changed.Select(_ => Unit.Default), 
         _collection.ItemChanged.Select(_ => Unit.Default))
    .Select(_ => _collection.Sum(x => x.NumberToSum));

另一种对这样的聚合不太好但也非常有用的方法是ActOnEveryObject,它将对列表中的每个项执行一个块,并正确处理初始化,重置和移动