tl;dr
- Implement
INotifyPropertyChanged
and you can use .WhenAny()
ReactiveObject
can be used for both ViewModel and Model classes. It is literally just a "reactive" object. i.e. like System.Object
. (Don't believe me? Check out the source.)
- Use
ObservableAsPropertyHelper
for calculated properties that are dependent on other reactive properties or events. (See the docs)
The Answer
There are a couple of ways you could handle this, but in particular it seems like you want to reason out a couple things:
- How can I take advantage of reactive capabilities without migrating from another framework that I'm using?
- Should
ReactiveObject
just be limited to ViewModels? Is it useful in normal Model classes?
First as you've already noticed, a lot of the draw of ReactiveUI comes with the powerful capabilities contained in .WhenAny()
. Can you use these methods between frameworks, yes! The .WhenAny()
, .WhenAnyValue()
, .WhenAnyObservable()
methods work on any object that implements INotifyPropertyChanged
. (Related Docs)
In fact, it's likely that your existing framework already implements INotifyPropertyChanged
on many of their own types, so .WhenAny()
naturally extends to work on those objects seamlessly. You almost never actually need a ReactiveObject
. It just makes your life easier.
Note: This is actually one of the core values of ReactiveUI. That at the core, ReactiveUI is really just a bunch of extension methods designed to make working with observables easier in the existing .Net world. This makes interoperability with existing code one of ReactiveUI's most compelling features.
Now, should ReactiveObject
be used for normal "dumb" models? I guess it depends on where you want the responsibilities to lie. If you want your model class to only contain normalized state and no logic at all, then probably not. But if your model is designed to handle both state and domain-related logic, then why not?
Note: There's a larger philosophical debate here about the single responsibility principal, software architecture, and MVVM, but that's probably for Programmers SE.
In this instance, we care about notifying listeners about updates to some calculated properties such as TotalLength
. (i.e. our model contains some logic) Does ReactiveObject
help us do this? I think so.
In your scenario, we want TotalLength
to be computed from DistancesBetweenXCoordinates
whenever an element is added or changed or something. We can use a combination of ReactiveObject
and ObservableAsPropertyHelper
. (Related Docs)
For example:
class Model : ReactiveObject
{
// Other Properties Here...
// ObservableAsPropertyHelper makes it easy to map
// an observable sequence to a normal property.
public double TotalLength => totalLength.Value;
readonly ObservableAsPropertyHelper<double> totalLength;
public Model()
{
// Create an observable that resolves whenever a distance changes or
// gets added.
// You would probably need CreateObservable()
// to use Observable.FromEventPattern to convert the
// CollectionChanged event to an observable.
var distanceChanged = CreateObservable();
// Every time that a distance is changed:
totalLength = distanceChanged
// 1. Recalculate the new length.
.Select(distances => CalculateTotalLength(distances))
// 2. Save it to the totalLength property helper.
// 3. Send a PropertyChanged event for the TotalLength property.
.ToProperty(this, x => x.TotalLength);
}
}
In the above, TotalLength
would be recalculated every time that the distanceChanged
observable resolves. This could for example be whenever DistanceBetweenXCoordinates
emits a CollectionChanged
event. In addition, because this is just a normal observable you could have the calculation occur on a background thread, allowing you to keep the UI responsive during a long operation. Once the calculation is done, the PropertyChanged
event is sent for TotalLength
.