I have a question which is an extension of the following question raised on this site.
Is there a more elegant way to merge observables when return type is unimportant?
I have an IObservable<Unit>
(lets say X
), a reactive collection (Y
) and a property (Z
). Return type is not important. I just want to subscribe when any of these change.
I know how to observe all 3 and Subscribe
using Observable.Merge
as below.
Observable.Merge(X, Y.Changed, ObservableForProperty(Z).Select(_ => Unit.Default)).Subscribe(..)
And it works.
However, when I try to use WhenAny(...,....,....).Subscribe()
, the subscribe does not get triggered when my X
changes. What is the syntax for doing the above using WhenAny(...)
rather than Observable.Merge(..)
??
I prefer to use WhenAny(....)
because I am using ReactiveUI
in other places.
Example:
Say I've got a class derived from ReactiveObject
with following properties.
public class AnotherVM : ReactiveObject
{
public bool IsTrue
{
get { return this.isTrue; }
set { this.RaiseAndSetIfChanged(x => x.isTrue, ref this.isTrue, value); }
}
public IObservable<Unit> Data
{
get { return this.data; }
}
public ReactiveCollection MyCol
{
get { return Mycol; }
}
}
public class MyVM : ReactiveObject
{
MyVM
{
// do WhenAny or Observable.Merge here....
}
}
I want to observe the above properties in AnotherVM
class using Observable.Merge(..)
or WhenAny(...)
in MyVM
class. I found that I do not always get a notification when I subscribe to the above in MyVM
using WhenAny(...)
or Merge(...)
when either of the 3 properties change.
WhenAny
is not for monitoring across sets of arbitrary observables, it's for monitoring the properties of an object supported by ReactiveUI (like a ReactiveObject or reactive collection).
For the general case of combining changes in observable streams, Observable.Merge
is the right way to go.
EDIT
I note that you have declared the Data and MyCol properties read only. If you use a Merge
like this:
Observerable.Merge(this.WhenAnyValue(o=>o.IsTrue, v=>Unit.Default),
this.Data,
this.MyCol.CollectionChanged.Select(v=>Unit.Default))
...then you must be careful not to change the backing fields. If you do, then you will get missing events - maybe this is what is happening?
In that case you would need to wire up those properties to RaiseAndSetIfChanged
and use a Switch
to keep track - e.g. if this.data
could change then you would need (I'm using ReactiveUI 5 + .NET 4.5 here in case the RaiseAndSetIfChanged syntax looks odd):
public IObservable<Unit> Data
{
get { return this.data; }
private set { this.RaiseAndSetIfChanged(ref data, value); }
}
and your merge would be something like:
Observerable.Merge(this.WhenAnyValue(o=>o.IsTrue, v=>Unit.Default),
this.WhenAnyObservable(x => x.Data),
this.MyCol.CollectionChanged.Select(v=>Unit.Default))
WhenAnyObservable is conceptually equivalent to this:
WhenAny(x => x.Data, vm => vm.Value).Switch()
using Switch to flip over to the latest value of Data
when it changes. Don't forget to use the setter to change values of data!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With