Inside XPF: Reactive Properties

Updated 2010-09-20: Reflects the removal of the generic type parameter TOwner from ReactiveProperty (starting Build 20).

Tetrahedral reflection by fdecomite

One of the design principles behind XPF has always been to provide a development experience that is familiar to Silverlight developers.  Silverlight’s Dependency Property plays an important role in making that development experience so good.  They enable most of the exciting functionality that both Silverlight and WPF bring to the table.

Pipes and Sockets

XPF introduces Reactive Properties that share some of the behaviour and familiarity of Dependency Properties and add the ability to behave like “sockets for pipes”.  Places where you can plug single- or bi-directional “pipes” that channel data asynchronously between your application’s objects.

Modern UI frameworks are heavily asynchronous so that stuff can happen in the background without making the UI unresponsive.  Silverlight helps enable this by providing asynchronous-only APIs for potentially “long-running” activities.  More and more, UI applications are built using objects that communicate with each other asynchronously.

Active Properties

Whilst regular C# Properties are mostly “passive”, Dependency Properties in Silverlight (and their sisters Attached Properties) are most definitely “active”.  Their “effective” values “depend” on their environment.  Instead of providing access to a single value in a backing field, they will actively seek out the most appropriate value for you at the time you request it.  Is an animation in progress?  Yes? Then that’s the value.  No?  Has the value been explicitly set?  OK, use that.  If not, is there a binding in place?  Is the binding one-way? OK, get the value from the source object.  Is there a relevant style to get the value from?  Can the value be inherited from an ancestor in the visual tree?  Also, if it’s an Attached Property it really belongs to an ancestor in the Visual Tree.

Each time a value is requested a complex workflow is instigated to work out what the value should be.  It’s great, it works really well and it’s behind all the Animation, Data Binding, Styling and Layout in Silverlight and WPF.

Reactive Properties

One of the most exciting things that’s happening in the world of software development at the moment is the evolution of Microsoft’s superb Reactive Extensions for .Net (Rx).  The team behind it discovered a duality between IEnumerable/IEnumerator and IObservable/IObserver which is not only extremely elegant, but also proves to be really useful in enabling pluggable, queryable, composable asynchronous pipes.

The same duality exists between Silverlight’s Dependency Properties and XPF’s Reactive Properties.  The former fetch the right value when asked to do so, the latter are given the right value when the time is right.  Data can be pushed to Reactive Properties through pipes – e.g. through a pipe from a data binding or through a pipe from an animation framework.  Data can also be pushed from the Reactive Property to any number of interested objects through pipes that can be joined, split, queried, filtered and processed in any number of ways.  All thanks to the genius of the guys behind Rx.

Show me some code

You’re probably familiar with the way in which Silverlight’s Dependency Properties are registered statically in a central registry.  XPF’s Reactive Properties are declared in a similar same way.  In the following code snippet, UIElement registers its Height Property with a default value of “Not a Number” and a call-back, which in this case, is just a static generic method that indicates to the property owner (UIElement) that it needs to re-measure when the value changes.  Notice that the generic Register method is typed for the property type (of double) allowing for strongly typed get/set accessors (no casting required):

public static readonly ReactiveProperty<double> HeightProperty =    ReactiveProperty<double>.Register("Height", typeof(UIElement),
        double.NaN, ReactivePropertyChangedCallbacks.InvalidateMeasure);

public double Height
{
    get
    {
        return this.GetValue(HeightProperty);
    }

    set
    {
        this.SetValue(HeightProperty, value);
    }
}

Attached Properties are declared in exactly the same way – the only difference is that the property accessors (which are also strongly typed) are static:

public static readonly ReactiveProperty<double> LeftProperty = 
    ReactiveProperty<double>.Register("Left", typeof(Canvas),
        double.NaN, ReactivePropertyChangedCallbacks.InvalidateArrange);

public static double GetLeft(IElement element)
{
    if (element == null)
    {
        throw new ArgumentNullException("element");
    }

    return element.GetValue(LeftProperty);
}

public static void SetLeft(IElement element, double value)
{
    if (element == null)
    {
        throw new ArgumentNullException("element");
    }

    element.SetValue(LeftProperty, value);
}

Data Binding with Reactive Properties

The next post in the “Inside XPF” series will go into data binding in a lot more detail, but this excerpt from the BDD specs in XPF’s codebase shows how you can bind a property on a source (that implements INotifyPropertyChanged) to a Reactive Property on an element.  Notice how the binding is simply an IObservable of double.  You can plug anything that implements IObservable<double> into the property directly using the Bind() method.

[Subject(typeof(DependencyObject))]
public class when_a_binding_is_one_way
{
    private const double ExpectedWidth = 10d;

    private static TestBindingObject source;

    private static Border target;

    private Establish context = () =>
        {
            source = new TestBindingObject(); // implements INotifyPropertyChanged
            target = new Border();

            IObservable<double> fromSource = BindingFactory.CreateOneWay(source, o => o.Width);
            target.Bind(UIElement.WidthProperty, fromSource);
        };

    private Because of = () => source.Width = ExpectedWidth;

    private It should_update_the_target_property_with_the_correct_value =
        () => target.Width.ShouldEqual(ExpectedWidth);
}

Summary

Properties in XPF work natively with the new IObservable and IObserver interfaces in .Net 4.0, and their implementations in the Rx Framework.  This really improves the developer experience, because now you can connect up your UI properties to any asynchronous source.  Imagine being able to asynchronously bind your Reactive Properties directly to WebClient’s async Download methods?

Download the latest build and have a play!

Inside XPF Series: