Posts Tagged ‘inside xpf’

13
Sep
2010

Inside XPF: Data Binding

by Stuart Harris

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

Neuroanatomical Connectivity Macaque by Arenamontanus In the previous post in this series, we discussed XPF’s Reactive Properties – the counterpart to Silverlight’s Dependency Properties.  Reactive Properties allow you to data bind to any source that implements the new .Net 4.0 IObservable<T> (for one-way binding) and/or any source that implements IObserver<T> (for one-way-to-source binding).  Two-way binding is simply a combination of the two.

The Reactive Object

In Silverlight, all UI Elements are derived from DependencyObject which the MSDN documentation says “Represents an object that participates in the dependency property system”.  In XPF all UI Elements are derived from ReactiveObject which represents an object that participates in the reactive property system.  ReactiveObject not only stores the current values of all the element’s reactive properties, but also allows you to bind to them in either (or both) directions.

The Bind method has overloads that allow you to bind a ReactiveProperty one-way to an IObservable, one-way-to-source to an IObserver or two-way to both.

The Binding Factory

So what do you do if the object you want to bind to doesn’t expose an IObservable/IObserver?  This is where the BindingFactory comes in.  The BindingFactory will create IObservables and IObservers around properties on either the data context or a specified source.

Binding to reactive properties, or properties on POCOs and objects that implement INotifyPropertyChanged is fully supported.  This enables element-to-element binding in addition to the use of familiar binding patterns often used in WPF/Silverlight such as Model View ViewModel (MVVM).

The observable/observer that the BindingFactory creates for you is internally wrapped in a Binding object which allows for either immediate or deferred resolution of the binding.  Immediate resolution is used when a reference to the source is provided, but if you are binding to an element’s data context then the binding resolution is deferred until the start of the Measure layout phase.  Deferred bindings are great because they allow the data context to be set or changed after the element binding has been declared.  This is essential for controls that use templates such as the ItemsControl.  The Binding’s resolution is handled seamlessly.

All data binding scenarios are supported.  The following table shows the various combinations of source objects/properties in each mode.  Note that binding to POCO properties works in both directions, but without INotifyPropertyChanged (INPC), one-way binding effectively becomes “one-time”.

Data Context
/Source Object
Reactive
Property
INPC
Property
CLR
Property
One-way Supported Supported Supported Supported
(one-time)
One-way-to-source Not applicable Supported Supported Supported
Two-way Not applicable Supported Supported Supported
(one-time and one-way-to-source)

Show me some code

Here are the overloads to ReactiveObject’s Bind method:

Bind

You’ll notice that because it’s a generic method, the parameters are strongly typed to match the target ReactiveProperty specified.  The scenario in the screenshot above shows binding to a source reactive property on the element’s data context.  The specification for this scenario looks like this:

[Subject(typeof(ReactiveObject), "One Way")]
public class when_there_is_a_one_way_binding_to_a_property_on_the_data_context
{
    private const double ExpectedWidth = 10d;

    private static SourceObject source;

    private static ContentControl target;

    private Establish context = () =>
        {
            target = new ContentControl();

            IObservable<double> fromSource =
                BindingFactory.CreateOneWay<SourceObject, double>(SourceObject.WidthProperty);
            target.Bind(UIElement.WidthProperty, fromSource);

            source = new SourceObject();
            target.DataContext = source;
            target.Measure(Size.Empty);
        };

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

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

Because we’re binding to the control’s data context the binding resolution is deferred until the target is measured.

Using the Binding Factory

Here are some examples of how to create Bindings using the Binding Factory.  Those that involve the data context are all deferred and those where the source is specified are all immediate.  An IDualChannel simply encapsulates both an IObservable and an IObserver so that data can flow in both directions (to and from the source).

Notice that you only need to specify type parameters when there is not enough information in the method’s parameters for the compiler to infer the types involved:

To the Data Context

IObservable<double> fromSource =
    BindingFactory.CreateOneWay<double>();

IObserver<double> toSource =
    BindingFactory.CreateOneWayToSource<double>();

To a Reactive Property on the Data Context

IObservable<double> fromSource =
    BindingFactory.CreateOneWay<SourceObject, double>(SourceObject.WidthProperty);

IObserver<double> toSource =
    BindingFactory.CreateOneWayToSource<SourceObject, double>(SourceObject.WidthProperty);

IDualChannel<double> fromAndToSource =
    BindingFactory.CreateTwoWay<SourceObject, double>(SourceObject.WidthProperty);

To a Reactive Property on a specified Source

IObservable<double> fromSource =
    BindingFactory.CreateOneWay(source, SourceObject.WidthProperty);

IObserver<double> toSource =
    BindingFactory.CreateOneWayToSource(source, SourceObject.WidthProperty);

IDualChannel<double> fromAndToSource =
    BindingFactory.CreateTwoWay(source, SourceObject.WidthProperty);

To an INotifyPropertyChanged or CLR Property on the Data Context

IObservable<double> fromSource =
    BindingFactory.CreateOneWay<SourceObject, double>(o => o.Width);

IObserver<double> toSource =
    BindingFactory.CreateOneWayToSource<SourceObject, double>(o => o.Width);

IDualChannel<double> fromAndToSource =
    BindingFactory.CreateTwoWay<SourceObject, double>(o => o.Width);

To an INotifyPropertyChanged or CLR Property on a specified Source

IObservable<double> fromSource =
    BindingFactory.CreateOneWay(source, o => o.Width);

IObserver<double> toSource =
    BindingFactory.CreateOneWayToSource(source, o => o.Width);

IDualChannel<double> fromAndToSource =
    BindingFactory.CreateTwoWay(source, o => o.Width);

Clearing a Binding

To clear a binding simply call the ClearBinding() method on the relevant control, specifying the reactive property whose binding you want to clear:

element.ClearBinding(Border.BorderBrushProperty);

Because an instance of ReactiveObject can only have one binding per property, calling the Bind() method replaces any existing binding.  Internally it calls ClearBinding() before attaching the new binding.

When a binding is cleared, its subscription to the source is automatically disposed.  This releases any attached event handlers (created automatically in order to listen to PropertyChanged events) which helps prevent memory leaks.  This is a great feature of Microsoft’s Reactive Extensions (Rx) that frees the developer from worrying about having to un-hook event handlers when an observer is no longer interested in the event.

Even though there can only be one binding at a time to an element’s property, you can still merge multiple observables together using any of the Rx combinators (Merge, Zip, CombineLatest etc) if you want your reactive property to listen to more than one source.

Summary

We hope that the binding system in XPF is simple to understand and easy to use.  Whilst the underlying property system in XPF is push based (rather than pull), we’ve strived to keep the development experience familiar to Silverlight and WPF developers.

The tight integration with Reactive Extensions makes it extremely flexible and powerful.  The statically typed nature of the binding and property system makes it intuitive, discoverable and refactoring friendly.  The absence of any magic strings means that there should be no run-time binding exceptions when a deferred binding cannot be resolved.  Binding to nested properties is currently not supported, but we plan to introduce this using the same strongly typed lambda expression syntax shown above.

Please download the latest build, play about with it and feedback to us!

Inside XPF Series:

9
Sep
2010

Inside XPF: Reactive Properties

by Stuart Harris

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: