Click here to Skip to main content
15,867,756 members
Articles / Desktop Programming / WPF

Programmatic type-safe bindings

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
18 Sep 2012CPOL4 min read 13.6K   7   4
Using "{Binding X.Y.Z}" in a type-safe way in your code.

What's this article about?

Consider the following nested member expression:

C#
this.Person.Employer.Address  

Wouldn't it be great if you could write something like

C#
Observe(() => this.Person.Employer.Address, address => UpdateAddress(address)); 

with the meaning that UpdateAddress will be called automatically every time the expression changes?

Note that the change could come from any level, from Person to Address itself. This is exactly what XAML bindings do, and this article explains how to do it in code.

I'm using Silverlight - I expect this to work in WPF and WinRT as well, but I haven't tried this yet.

Why does this matter?

When coding in the MVVM paradigm one needs to implement view models that are bound against views written in XAML.

I find that those view models often themselves need to bind against other sources. There are two common scenarios:

  1. The view consists of subviews requiring respective sub-view-models and the outer view model needs to be notified of changes in such a sub-view-model.
  2. The models which contain the actual data and which are backing the view models have themselves "view model" traits - I'll explain what I mean by that with an example:

Take RIA Services. With this framework, (some) of your models will be auto-generated entity classes. They can actually play the role of view models themselves in simple cases, but let's assume one has a separate view model. When such an entity is updated by user interaction or a load operation, the view model sometimes must be notified of the change to update accordingly. Please take a look at the following diagram:

Image 1

The many entity classes are often a given: They simply closely model how the data is organized. Now imagine the view should display a prominent visual in the case where the address refers to the same house as the currently logged-in user. To implement that, one would need to track changes of this member expression in the view model:

C#
this.Person.Employer.Address

And a change could likely be triggered from:

  • the user by editing text or
  • the user by rejecting the current changes or
  • a completion of a load operation.

Wiring up all these probably very distinct code paths to trigger some update method in the view model is tedious and error prone. Why can't we just observe the property path just like we do in XAML bindings? What we want to write is simply something like "{Binding Person.Employer.Address}".

How is it done?

The key is to use the Binding object programmatically. This means that the behavior of our tool will be the same as that of XAML bindings and it will work in exactly the same cases.

Since the Binding object expects the property path as a string literal and we want to use type- and name-safe lambda expressions, the first step is a tool to generate property paths from such expressions.

C#
public class RootedPropertyPath<T>
{
    public Object Target { get; set; }
    public String Path { get; set; }

    public Binding ToBinding(BindingMode mode = BindingMode.TwoWay)
    {
        return new Binding(Path) { Source = Target, Mode = mode };
    }

    public static implicit operator System.Windows.PropertyPath(RootedPropertyPath<T> self)
    {
        return new System.Windows.PropertyPath(self.Path);
    }

    public static implicit operator String(RootedPropertyPath<T> self)
    {
        return self.Path;
    }
}

public static class RootedPropertyPath
{
    public static RootedPropertyPath<T> Create<T>(Expression<Func<T>> expr)
    {
        Expression currentExpression = expr.Body;

        List<String> lst = new List<String>();

        ConstantExpression ce;

        while (true)
        {
            ce = currentExpression as ConstantExpression;

            var me = currentExpression as MemberExpression;

            if (ce != null) break;

            if (me == null)
                throw new Exception(String.Format(
                    "Unexpected expression type {0} in lambda.", expr.GetType()));

            lst.Add(me.Member.Name);

            currentExpression = me.Expression;
        }

        lst.Reverse();

        return new RootedPropertyPath<T>() { Path = String.Join(".", lst), Target = ce.Value };
    }
}

Note that RootedPropertyPath<T> is already slightly more than a type-safe property path: We also collect the base object, the "target" of the binding right from the expression - hence the "rooted" in the name.

A path can now be constructed with

C#
var rootedPath = RootedPropertyPath.Create(() => this.Person.Employer.Address) 

and then

C#
var binding = rootedPath.ToBinding() 

gives us the binding we need to observe the nested member expression.

The Create method basically consists of the common trickery used for implementing INotifyPropertyChanged in a a type- and name-safe way.

The next step is to bind something. In order to do that, we need a DependencyProperty of the right type and bind that to the binding we have. That's what the following tool is for:

C#
public static class ObservableDependencyValue
{
    public static ObservableDependencyValue<T> Create<T>(
       Expression<Func<T>> expr, BindingMode mode = BindingMode.OneWay)
    {
        return new ObservableDependencyValue<T>().Bind(expr, mode);
    }
}

public class ObservableDependencyValue<T> : DependencyObject
{
    #region T ObservableDependencyValue<T>.Value = default(T)
    #region Boilerplate

    public T Value
    {
        get { return (T)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(T), 
        typeof(ObservableDependencyValue<T>), new PropertyMetadata(default(T), StaticHandleValueChanged));

    static void StaticHandleValueChanged(DependencyObject self, DependencyPropertyChangedEventArgs args)
    {
        ((ObservableDependencyValue<T>)self).HandleValueChanged((T)args.OldValue, (T)args.NewValue);
    }

    #endregion

    void HandleValueChanged(T oldValue, T value)
    {
        Notify();
    }
    #endregion

    public ObservableDependencyValue()
    {
    }

    public ObservableDependencyValue(BindingBase bindingBase)
    {
        Bind(bindingBase);
    }

    public ObservableDependencyValue<T> Bind(BindingBase bindingBase)
    {
        BindingOperations.SetBinding(this, ValueProperty, bindingBase);

        return this;
    }

    public ObservableDependencyValue<T> Bind(Expression<Func<T>> expr, 
           BindingMode mode = BindingMode.OneWay)
    {
        var path = RootedPropertyPath.Create(expr);

        return Bind(new Binding(path.Path) { Source = path.Target, Mode = mode });
    }

    public void Notify()
    {
        if (ValueChanged != null) ValueChanged(Value);
    }

    public event Action<T> ValueChanged;
}

ObservableDependencyProperty<T> exists just for its one property "Value" that can be bound to an arbitrary binding and report changes. For convenience, the creation of the binding is already built into the class - we just need to pass the member expression itself in the factory method of ObservableDependencyValue.

We're now almost there. If we write our view model like this

C#
public class PersonViewModel : OurViewModelBase
{
    public PersonViewModel()
    {
        var observableDependencyProperty = ObservableDependencyValue.Create(() => 
                       this.Person.Employer.Address);

we have an object that features an event fired on a change of the member expression. This can be used directly:

C#
observableDependencyProperty.ValueChanged += address => UpdateAddress(address);

But there is one major catch: We must make sure that our observableDependencyProperty will live as long as our view model does. Thus, we need to store a reference in our view model:

C#
    observableDependencyPropertyReference = observableDependencyProperty;
  }

  Object observableDependencyPropertyReference;
}

That is slightly unfortunate - but since it's common practice to have a custom common base for view models, I suggest adding something to register such objects there:

C#
public class OurViewModelBase
{
    ...

    protected void Observe<T>(Expression<Func<T>> expr, Action<T> changed)
    {
        var odv = ObservableDependencyValue.Create(expr);
        odv.ValueChanged += changed;
        lifetimeObjects.Add(odv);
    }

    List<Object> lifetimeObjects = new List<Object>();

    ...
}

And that is the promised syntax and the conclusion of this article - I hope you find the technique useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Germany Germany
I'm an IT freelancer.

Comments and Discussions

 
QuestionYou can provide kind of example application for this Pin
MahBulgariaRebornAgain2-Apr-15 21:48
MahBulgariaRebornAgain2-Apr-15 21:48 
GeneralMy vote of 5 Pin
Harry von Borstel20-Sep-12 3:06
Harry von Borstel20-Sep-12 3:06 
GeneralMy vote of 5 Pin
Kanasz Robert19-Sep-12 4:35
professionalKanasz Robert19-Sep-12 4:35 
GeneralRe: My vote of 5 Pin
Jens Theisen19-Sep-12 6:38
Jens Theisen19-Sep-12 6:38 
Thank you!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.