ToProperty and Output Properties
One of the core features of ReactiveUI is to be able to convert properties to
Observables, via WhenAny, and to convert Observables into Properties, via a
method called ToProperty. These properties are called Output Properties in
ReactiveUI, and they are a huge part of using the framework effectively.
Consider a color picker dialog - this dialog might have four properties:
- Red
- Green
- Blue
- FinalColor
The 4th property, FinalColor, isn't like the others however. It isn't a
read-write property, its value is determined by the sum of the other three
properties. In other frameworks, this would also be a read-only property, which
can now be set by multiple parts of the code. This is the beginning of UI
spaghetti code ugliness.
ReactiveUI allows you to explicitly describe the dependencies between properties, in a way that makes it difficult to write incorrect, difficult to debug spaghetti code.
Basic Usage
First, we need to create an Output Property, using a class called
ObservableAsPropertyHelper<T>. All Output Properties are always written the
same way, it is 100% boilerplate code.
ObservableAsPropertyHelper<Color> finalColor;
public Color FinalColor {
get { return finalColor.Value; }
}
Note that there is no setter for FinalColor. We'll describe how the color
changes using Observables instead. In the Constructor, we'll set it up:
var colorValues = this.WhenAnyValue(x => x.Red, x => x.Green, x => x.Blue,
(r,g,b) => new {r,g,b})
.Select(x => new Color(x.r, x.g, x.b));
colorValues.ToProperty(this, x => x.FinalColor, out finalColor);
Lazy Observation
It's important to know the semantics of ToProperty - you should conceptualize
this method as similar to Subscribe: it causes the Observable to be evaluated,
just like foreach causes Enumerables to be evaluated.
However, beginning with ReactiveUI 6.0, ToProperty is lazy - it doesn't
Subscribe until someone (usually a View Binding) requests the property for the
first time (via Value). ToProperty is morally similar to the following code:
// This is similar
theObservable.ToProperty(this, x => x.Foo, out foo);
//
// ...to this
//
var backingFoo = theObservable
.Do(x => Foo = x)
.Publish()
.RefCount();
public string Foo {
get {
backingDisposable = backingDisposable ?? backingFoo.Subscribe();
return foo;
}
}
Should you have problems with ToProperty "missing" events, the easiest way to resolve it is via Concat'ting a canned value. For example:
// Has problems, because CanExecuteObservable is Hot
someCommand.CanExecuteObservable
.ToProperty(this, x => x.CanExecute, out canExecute);
// Hack around via making the Observable cold
Observable.Defer(() => Observable.Return(someCommand.CanExecute(null)))
.Concat(someCommand.CanExecuteObservable)
.ToProperty(this, x => x.CanExecute, out canExecute);