Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine multiple IObservable<bool> to form composite bool subscription value

I would like to create a kind of Rx based ICommand for WPF. What I would like to do is to be able to control the CanExecute by combining ANY number of IObservable<bool> streams.

The way I would like to work is that I would like to use the latest combined logically ANDed values of all of the predicates, and use that to control the bool ICommand.CanExecute(object parameter) method implementation. I do not want to wait for all predicates to yield, it should use whatever one of the source predicate streams OnNexts (yields a value).

I have got a bit stuck with trying to work out how to wire it up such that any of the predicates should cause the ICommand.CanExecute to yield a new value.

Forgetting the actually ICommand implementation for a minute (as my question is more about the Rx side of things), can anyone suggest how I can wire up a bunch of predicates ( IObservable<bool>) that yield whenever the underlying things that create their streams change, but will also work in unison to create an overall end bool value that I can subscribe too. The end value would be a logical AND of the current predicate stream values.

I am hoping I do not need to subscribe to all the predicate streams, and am hoping for a cool operator within RX that I may have overlooked.

I know I can Merge streams, but that is not quite the behavior I am after, as that is just the latest value from the input streams that have been merged, I also know that I can CombineLatest, again this is not quite correct, as it will only yield when all the combined streams yield values.

What I want is the streams to be combined so any change will notify a subscriber, but I also want to know what the logical ANDing of the combined predicate IObservable<bool> streams is right now, such that I can drive the ICommand.CanExecute from this overall combined value.

I hope this makes sense.

Here is some skeleton code (I have left some commented out code which shows the thinking behind my Rx Command idea, as it may help to illustrate what I want to get working)

public class ViewModel : INPCBase
{
    private string title;
    private bool hasStuff;

    public ViewModel()
    {
        //Initialise some command with 1st predicate, and 
        // initial CanExecute value
        //SomeCommand = new ReactiveCommand(
        //    this.ObserveProperty(x => x.Title)
        //        .Select(x => !string.IsNullOrEmpty(x)), false);
        //SomeCommand.AddPredicate(this.ObserveProperty(x => x.HasStuff));
        //SomeCommand.CommandExecutedStream.Subscribe(x =>
        //    {
        //        MessageBox.Show("Command Running");
        //    });

        IObservable<bool> obsPred = this.ObserveProperty(x => x.Title)
          .Select(x => !string.IsNullOrEmpty(x))
          .StartWith(!string.IsNullOrEmpty(this.Title));
        IObservable<bool> obsPred2 = this.ObserveProperty(x => 
          x.HasStuff).StartWith(this.HasStuff);


        obsPred.Merge(obsPred2).Subscribe(x =>
            {
                //How do I get this to fire whenever obsPred OR 
                //obsPred2 fire OnNext, but also get a combined value (bool) 
                //of the AND of obsPred & obsPred2 (bearing in mind I may 
                //want more than 2 predicates, it should cope with any number of
                //IObservable<bool> predicates
            });
    }

    public string Title
    {
        get
        {
            return this.title;
        }
        set
        {
            RaiseAndSetIfChanged(ref this.title, value, () => Title);
        }
    }


    public bool HasStuff
    {
        get
        {
            return this.hasStuff;
        }
        set
        {
            RaiseAndSetIfChanged(ref this.hasStuff, value, () => HasStuff);
        }
    }

}
like image 963
sacha barber Avatar asked Dec 04 '25 05:12

sacha barber


1 Answers

You are looking for the CombineLatest operator

ISubject<bool> obsPred  = new BehaviorSubject<bool>(false);
ISubject<bool> obsPred2 = new BehaviorSubject<bool>(false);

Observable.CombineLatest(obsPred, obsPred2, (a, b)=>a&&b)
        .DistinctUntilChanged()
        .Dump();

obsPred.OnNext(true);
obsPred2.OnNext(true);
obsPred2.OnNext(true);
obsPred.OnNext(true);

obsPred.OnNext(false);

This will output

False
True
False

The use of DistinctUntilChanged() will stop duplicate consecutive values being returned.

Obviously swap out the BehaviorSubjects for your property observables.

like image 180
Lee Campbell Avatar answered Dec 07 '25 02:12

Lee Campbell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!