如何在一个可观察对象上使用触发器来抑制另一个对象的触发器?

How to use trigger on one observable to suppress trigger on another?

假设我有两个可观察对象 Obs1 和 Obs2。我想要 Obs1 上的触发器来抑制 Obs2 上的后续触发器(下面的大理石图)。我该怎么做?

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

具体来说,我有一个 class 具有两个属性,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); }
    }
}

看来您要做的是破译 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 这不是好的做法,因为副作用很糟糕!

这会生成您想要的值:

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