如何在一个observable上使用触发器来抑制另一个observable上的触发器?

时间:2016-04-18 15:48:08

标签: c# .net system.reactive reactive-programming reactiveui

假设我有两个可观察量Obs1和Obs2。我希望Obs1上的触发器能够抑制Obs2上的后续触发(下面的大理石图)。我该怎么做?

Obs1---x---x--x----
Obs2----yyy-yy-yyyy
Rslt-----yy--y--yyy

具体来说,我有一个包含两个属性的类,Target和SelectedItem。设置目标时,应立即根据目标上的SpecialValue属性设置SelectedItem。用户应该能够更改选择,在这种情况下,新值会传播回目标。仅当用户更改值时,SelectedItem才会传播回目标;但是,当目标设置时,该值会传播回目标 - 这是我正在尝试修复的不良行为。

(SelectionViewModel利用ReactiveUI,但我们模仿Prism的SetProperty方法以帮助迁移.BindToProperty只是一种帮助它完成它所说的方法。)

sealed class SelectionViewModel
{
    internal SelectionViewModel( )
    {
        this.WhenAnyValue(x => x.Target).Where(t => t != null)
            .Select(_ => Target.SpecialValue)
            .BindToProperty(this, x => x.SelectedItem);

        this.WhenAnyValue(x => x.Target).Where(t => t != null)
            .Select(_ => this.WhenAnyValue(x => x.SelectedItem).Skip(1))
            .Switch()
            .BindToProperty(this, x => Target.SpecialValue);
    }

    private MyClass _selectedItem;

    public MyClass SelectedItem
    {
        get { return _selectedItem; }
        set { SetProperty(ref _selectedItem, value); }
    }

    private ITarget _target;

    public ITarget Target
    {
        get { return _target; }
        set { SetProperty(ref _target, value); }
    }
}

2 个答案:

答案 0 :(得分:2)

这会产生您想要的值:

var Obs1 = new Subject<string>();
var Obs2 = new Subject<string>();

var query = Obs2.Publish(pobs2 => Obs1.Select(x => pobs2.Skip(1)).Switch());

query.Subscribe(Console.WriteLine);

Obs1.OnNext("X0");
Obs2.OnNext("Y0");
Obs2.OnNext("Y1");
Obs2.OnNext("Y2");
Obs1.OnNext("X1");
Obs2.OnNext("Y3");
Obs2.OnNext("Y4");
Obs1.OnNext("X2");
Obs2.OnNext("Y5");
Obs2.OnNext("Y6");
Obs2.OnNext("Y7");
Obs2.OnNext("Y8");

我明白了:

Y1
Y2
Y4
Y6
Y7
Y8

答案 1 :(得分:0)

您尝试做的似乎是解密SelectedItem是由IObservable还是由用户设置。我想知道共享状态的一种方法是使用标志来通知SelectedItem对象或用户的更改是否正在更改Target,所以:

sealed class SelectionViewModel
{
    private bool _setByTarget = false;

    internal SelectionViewModel( )
    {
        this.WhenAnyValue(x => x.Target).Where(t => t != null)
            .Select(_ => Target.SpecialValue)
            .Do(_ => _setByTarget = true)
            .BindToProperty(this, x => x.SelectedItem);

        this.WhenAnyValue(x => x.Target)
            .Where(t => t != null && !_setByTarget )
            .Select(_ => this.WhenAnyValue(x => x.SelectedItem))
            .Switch()
            .BindToProperty(this, x => Target.SpecialValue);

        this.WhenAnyValue(x => x.Target)
            .Where(!_setByTarget)
            .Subscribe(_ => _setByTarget = false);
    }

    private MyClass _selectedItem;

    public MyClass SelectedItem
    {
        get { return _selectedItem; }
        set { SetProperty(ref _selectedItem, value); }
    }

    private ITarget _target;

    public ITarget Target
    {
        get { return _target; }
        set { SetProperty(ref _target, value); }
    }
}

此解决方案使用副作用诱导.Do,这不是好习惯,因为副作用很差!