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

Automatic Data Binding via Expression Trees

Rate me:
Please Sign up or sign in to vote.
4.11/5 (4 votes)
29 Jan 2016CPOL14 min read 15.2K   185   10  
Experimenting with C# expression trees for creating data-bound UI's from code.

Introduction

I arrived at the content of this article while trying to effectively use the Window Presentation Foundation (WPF) framework and Model-View-ViewModel (MVVM) principles. During this process, I discovered that many aspects of the WPF framework are implemented using runtime binding and sacrifice compile-time safety and static checking that might be available otherwise. A wide spread example of this is data binding. That is, XAML constructs such as this:

XML
<TextBlock Text="{Binding Path=SomeTextProperty}"/>

The benefit of this construct is that it provides a relatively concise way to express that some control's property should be bound to another property in the programmer's model (or "view model"). The downside, however, is that the "ModelProperty" string is not validated against the target model class until runtime. So if I've miss-typed ModelProperty as "SchmodelProperty", I won't find this out until I exercise the specific portion of the UI that contains the binding. Always a fan of maximizing the computer's ability to tell me when I've done something human (i.e., something dumb), I set about experimenting with ways to achieve the same expressiveness without sacrificing static checking. The remainder of this article describes one possible approach, and ultimately enables declarations of this sort:

C#
new TextBlock() { Text = model.SomeTextProperty }

At face value, this is just an expression that returns a newly created TextBlock object with the Text property statically set to the value of a property defined by the model. However, with the use of a C# feature known as Expression Trees, it is possible to turn this expression into another one that provides similar functionality as the XAML snippet above.

Background

The bulk of this article talks about transforming expression trees, but also touches some on Language Integrated Query (LINQ). Both of these topics are relatively deep, and some prior understanding of these might be needed. Of course, a cursory understanding of MVVM in WPF is also helpful, but not necessarily required. Throughout the article, the terms model and view model are used interchangeable, as the difference isn't really important in this context.

Change Notification

The standard way to propagate change from the model to the UI in WPF is via the use of the IPropertyNotifyChanged interface. This interface requires an event to be fired whenever any property of the model changes, and the event data contains the name of the property as a string. This is all fine, but it also presents another case in which information known at compile-time is tossed away and left for runtime to be validated. A common model is to develop a base class with implements a OnPropertyChanged(string propertyName) method that derived classes can call to raise the event. Unfortunately, the propertyName parameter must be specified as a string, which can easily be miss-typed by the programmer. There are at least a few different work-around for this:

  • Use code-generation tools at build time to automatically generate "notifying" properties.  (Example)
  • Use lambda expressions and extension methods to reduce the amount of code required.  (Example)
  • Use the [CallerMemberName] attribute.  (Example)

Although these are all fine ways to handle this, I decided to try another approach and define a generic Property<T> class that can provide change notification automatically, and is simple to use. The implementation found in the code is not compatible with the WPF data binding mechanism, however. Since this was only an experiment, I didn't have any qualms about going this route, but I expect many readers may raise an eyebrow or two. The overall approach could be implemented in a way that works with INotifyPropertyChanged instead, but is slightly more complicated. If this approach is desired, it is left as an exercise for the reader. Otherwise, the implementation of Property<T> is shown below.

C#
public class Property<T>
{
    public event EventHandler<ValueChangedEventArgs<T>> ValueChanged;

    public static implicit operator T(Property<T> p)
    {
        return p.val;
    }

    public Property(T value)
    {
        val = value;
    }

    public Property()
    {
        val = default(T);
    }

    private T val;
    public T Value
    {
        get { return val; }
        set
        {
            if (!EqualityComparer<T>.Default.Equals(value, val))
            {
                val = value;
                ValueChanged.Raise(this, new ValueChangedEventArgs<T>(val));
            }
        }
    }
}

The implicit operator T overload is optional, but allows Property<T> types to be used directly in assignment expressions. For example, assuming model.Text is a Property<string> object:

C#
textBox.Text = model.Text

Using the Property<T> type in a model class can be very simple like this:

C#
public class Model
{
    // Assign an initial value to the Property<string>
    public Property<string> Text = new Property<string>("initial value");

    public void Method()
    {
        // Assign the Property<string>'s value, which causes the ValueChanged
        // event to be fired.
        Text.Value = text;
    }
}

Working with Expression Trees

Before diving straight into the specific expression tree transformations used for data binding, I'd like to discuss some abstract building blocks used in the code. These constructs can be used much more broadly for different sorts of queries or transforms over expression trees. To build them, I used some standard LINQ operators so that I could leverage the query syntax available in C#. These parts of the code are mostly built around the ExpressionVisitor class available in the System.Linq.Expressions namespace. This class provides all the logic needed to traverse as well as transform expression trees, even without being aware of or understanding all the various types of expression nodes that can be present. Unfortunately, using ExpressionVisitor directly requires a special implementation of a derived class. The LINQ operators presented allow lots of different types of operations over expression trees to be implemented without a custom class implementation.

LINQ Operators for Tree Structures

The approach I usually take when writing LINQ operators is to try and identify a generic interface ISomeInterface<T> that supports the common semantics of all the operations I want to support. I do this while thinking about the form of the various LINQ operators, and the fact that they involve functions over type T. Lastly, I try to make sure that the English meaning of the operators at least come close to making sense in the context! Although, sometimes this is not quite possible, and some abuse can be tolerated. In the case of expression tree manipulation, I identified two fundamental operations that were common to many different specific transformations I could imagine:

  • Top down visitation
  • Bottom up visitation

The only difference between the two are the order in which tree nodes are visited, i.e., parents before children and vice-versa, respectively. Top-down visitation has the benefit that large sub-trees can be skipped if they aren't involved in the transform. In the extreme case, if there is something that is particular about the root node that implies no other nodes need to be examined, you can save a lot of processing. On the other hand, with bottom-up processing, it is too late to skip a particular sub-tree because once you arrive at it, you've already visited all it's children. There is a benefit to bottom-up visitation when it comes to certain types of transformations, however. If you replace a parent node with a new node that contains the original node as a child, you can very easily create non-terminating code, as you would re-visit the original parent node after extending the tree. This is avoided altogether when visiting nodes in a bottom-up order. To see this, consider a very simple example: Start with this tree, called "XZY":

 X
/ \
Y Z

And visit from top to bottom, replacing Z with another "XZY" tree. The following tree would result after the first few visitations of the "Z" node:

 X       X         X
/ \     / \       / \
Y X     Y X       Y X
 / \     / \       / \
 Y Z     Y X       Y X
          / \       / \
          Y Z       Y X
                     / \
                     Y Z

You can see that this pattern will continue on infinitely. If we instead proceed in a bottom-up fashion, we stop at this tree with only a single replacement:

 X
/ \
Y X
 / \
 Y Z

Above, we've used "binary" trees (nodes with only two children), but note that expression trees in C# have nodes of many different types. Some have a fixed number of child nodes (e.g., binary expressions), but others have a variable number of nodes (e.g., arguments in a method call expression).

Comparison with IEnumerable

Since the built-in LINQ operators fit very well with the IEnumerable<T> interface, it is often times useful to compare a set of custom operators with the IEnumerable<T> semantics in order to help visualize and understand what each of the operators might provide in the new context. In our case, we are considering the process of visitation in contrast with enumeration. They are somewhat similar, and in fact it is pretty natural to imagine an IEnumerable<T><expression> type that enumerates the nodes of a tree. However, we want to preserve the structure of the tree, as this may be important for transformations (unless one is only replacing single nodes with single nodes). Another way of saying this is that many different tree structures could produce the same IEnumerable<T> just by walking the nodes in a certain order.

<expression>However, using the IEnumerable<T> model as a start, lets imagine a set of LINQ query operators that apply functions over the nodes in a tree.  Instead of the enumerable interface, we want to follow the usual visitor pattern. That is, for each node, we want to supply a type that can "accept" every type of node that might exist in the tree. Typically, this is a class that implements a context-specific interface that has a method defined for each node type. The astute reader may at this point be wondering how this can be integrated with LINQ operators. After all, LINQ operators apply functions over some type T, not arbitrary interface implementations. However, these two concepts can be married together by taking advantage of the fact that (almost) all the nodes in an Expression tree have a common base class, Expression. Due to this fortunate occurrence, we can reduce an interface with a number of "AcceptExpressionTypeXyz" methods into a single function that operates over the Expression type.

LINQ Operator Set

I'm almost ready to present the set of operators that are implemented in the code and used for transforming expression trees. Before listing the operators themselves, we must define the common interface that captures the visitation semantics. We call this interface IVisitable<T>, where T is Expression or some type that derives from it (or even any type in some limited cases, as long as the final "select" operator in a query expression returns something that is assignable to Expression). The definition of IVisitable<T> is shown below:

C#
public interface IVisitable<T>
{
    Expression Visit(Func<T, Expression> accept);
}

That is, an IVisitable<T> has a single method that applies a function from T to Expression that returns a transformed expression. It is implied that any implementation of IVisitable<T> contains a reference to some "source" expression to be transformed.

Next, we need some functions that take Expression objects and turn them into types that implement IVisitable<T> (IVisitable<Expression><expression>, for starters). This is where we can capture the concept of traversal direction:

C#
public static IVisitable<Expression> TopDown(this Expression e);
public static IVisitable<Expression> BottomUp(this Expression e);

Each of these returns an IVisitable<T> that visits the supplied Expression in the appropriate order. An important thing to note here is that once a direction is chosen, all LINQ operators applied to the returned IVisitable<T> are bound to the same direction. That is, within a single LINQ query expression, there can be only one direction (unless a separate, nested query is used). Now for the operators:

C#
IVisitable<TResult> Select<T, TResult>(this IVisitable<T> source, Func<T, TResult> selector)

Returns a new IVisitable<TResult> that when visited, receives the nodes as transformed by the supplied selector.

C#
IVisitable<T> Where<T>(this IVisitable<T> source, Func<T, bool> predicate)

Returns a new IVisitable<T> that skips over any nodes (but not their children) for which the predicate returns false.

C#
IVisitable<T> OfType<T>(this IVisitable<Expression> source)

Returns a new IVisitable<T> that skips over nodes (but not their children) that aren't of type T, and also casts the node to type T before passing it to the Func<T, Expression> delegate during visitation.

C#
IVisitable<Expression> TakeWhile(this Expression source, Func<Expression, bool> predicate)

Very similar to the "where" operator, but this one does skip over the descendants of nodes that don't match the predicate. This operator is useful if you want to skip over entire sub-trees.

C#
Expression AsExpression<T>(this IVisitable<T> visitable)

Returns the underlying Expression object, possibly transformed.

C#
List<T> ToList<T>(this IVisitable<T> visitable)

Returns a list of objects of type T by visiting the underlying expression tree and placing the visited nodes in a list in order of visitation.

Data Binding Transforms

We are now ready to start transforming expression trees! I believe there are lots of alternative directions and design choices available at this point, so the reader is encouraged think critically while digesting this section. I am working with WPF controls, so I've made certain assumption with that in mind. As mentioned in the introduction, the basic idea behind the transform is take expressions of this form:

C#
new SomeWpfControl() {
  // ... member initialization expressions ...
}

And turn it into a new expression of this form (pseudo-code):

C#
{
    var control = new SomeWpfControl() { ... };
    foreach (memberInitExpression in newExpression)
    {
        foreach (modelProperty in memberInitExpression)
        {
            modelProperty.ValueChanged += (sender, args) => evaluate(memberInitExpression)
        }
    }
    return control;
}

That is, install a ValueChanged event handler for every model property that is part of the right-hand side of a control property initializer. Whenever any of these model properties are updated, the initializer expression is re-evaluated in order to update the control's property. Before we can look at the final transformation found in the code, there are a couple of wrinkles I'm going to throw in here. They are both valuable features, but do add some complexity to the implementation.

Limited Re-evaluation Scope

This is a sort of optimization that is intended to better handle cases in which member initialization expressions contain WPF controls themselves. An example is when using a custom button, like this:

C#
new Button() {
    Enabled = model.Enabled,
    Content = new TextBlock() {
        Text = model.Text
    }
}

The idea here is that when model.Text changes, it is not necessary to re-create the TextBlock, even though it is ultimately a part of the expression used to assign the Button.Content property. This is not something that should be generally true for arbitrary expressions, but for WPF controls, it is perhaps a reasonable assumption that any change handling that is required when a child control is updated is already handled by the controls themselves. The net result of this is that when looking for model dependencies within an assignment expression, any new expressions for WPF controls are skipped.

In the source code, there is a method

C#
Dependencies<TSkip>(MemberBinding binding, Type dependType) 

that implements this idea.  It does so by utilizing the TakeWhile operator in order to skip embedded new expressions of type TSkip.  In our case, TSkip is UIElement, and dependType is typeof(Property<>).

Weak Event Pattern

This design pattern is commonly used whenever event subscribers have much shorter lifetimes than their associated publishers. A type of memory leak can occur if WPF controls are re-created while event handlers are registered. Because the handler has a reference to the control, and (when using standard event registration mechanism, i.e., "+=" operator) the model has a reference to the handler, the control cannot be garbage collected during the lifetime of the model. When using the Weak Event Pattern (as WPF does), a proxy class registers handlers with the publisher and maintains its own table of weak references to the subscriber's handlers, so it doesn't prevent the subscriber from being garbage collected. 

The .NET 4.5 class library provides a WeakEventManager(TSource, TEventArgs) class that we can use.  However, because we are using a lambda closure as the handler, we have to maintain our own table of strong references to the event handlers.  Otherwise, the handler will be immediately eligible for garbage collection. To accomplish this, we create a class called BoundContent, that inherits from System.Windows.Controls.ContentControl. This special content control transforms an expression, compiles it, and keeps a reference to the compiled code and the table of event handlers.

The Transform

The bulk of the code for the expression transform that inserts event handlers is shown below.  It is a little lengthy, but I think demonstrates the utility of the LINQ operators defined above.

C#
public static BoundExpression<TResult> AddBindings<TBase, TResult>(this Expression<Func<TResult>> expr)
{
    // Generate a visitable that visits only MemberInitExpressions 
    // for type T, i.e., expressions of the form "new T(){ ... }".
    // It is very important to use BottomUp() so that nodes can be 
    // replaced without visiting the new nodes.
    var visitable =
        from e in expr.BottomUp()
        where e.IsMemberInit<TBase>()
        select e as MemberInitExpression;

    // This list is used to store references to the handlers that are 
    // created when the expression is evaluated.
    var table = new BindingTable();
    int handlerIndex = 0;

    // Transform each MemberInitExpression into a new expression that,
    // when evaulated, adds ValueChanged handlers which update the 
    // object created by the new expression.
    var modifiedExpression = visitable.Visit(init =>
    {
        // Create a variable that will store the constructed object.
        var newObjectVar = Expression.Variable(init.NewExpression.Type);

        // For each member binding (e.g., assignment expression 
        // within the braces), enumerate all the sub-expressions that 
        // are of a Property<T> type.  For each of the Property<T> 
        // expressions, create a lambda expression that will serve as 
        // a ValueChanged event handler.
        var handlerRegistrations =
            from binding in init.Bindings
            from depend in binding.Dependencies<TBase>(typeof(Property<>))

            // (object sender, ValueChangedEventArgs<T>) formal parameters
            let sourceParameter = Expression.Parameter(typeof(object))
            let valueType = typeof(ValueChangedEventArgs<>)
                .MakeGenericType(depend.Type.GenericTypeArguments[0])
            let valueParameter = Expression.Parameter(valueType)

            // (sender, args) => ObjectProperty = expression;
            let lambda = Expression.Lambda(
                typeof(EventHandler<>).MakeGenericType(valueType),
                Expression.Block(
                    Expression.Assign(
                        Expression.Property(newObjectVar, binding.Member.Name),
                        // TODO: This only works for MemberAssignment bindings
                        ((MemberAssignment)binding).Expression)
                ),
                sourceParameter, valueParameter)

            // MethodInfo for WeakEventManager<...>.AddHandler method
            let addHandler = typeof(System.Windows.WeakEventManager<,>)
                .MakeGenericType(depend.Type, valueType)
                .GetMethod("AddHandler")

            // Create a block expression that 
            //  1. stores the handler in a variable
            //  2. adds it to the handlers list
            //  3. registers it using the WeakEventManager
            let lambdaVar = Expression.Variable(lambda.Type)

            select Expression.Block(
                new ParameterExpression[] { lambdaVar },
                Expression.Assign(lambdaVar, lambda),
                Expression.Call(
                    Expression.Constant(table),
                    typeof(BindingTable).GetMethod("AddHandler"),
                    Expression.Constant(handlerIndex++),
                    lambdaVar),
                Expression.Call(
                    addHandler,
                    depend,
                    Expression.Constant("ValueChanged"),
                    lambdaVar));

        // Finally, convert the original expression into a block expression that:
        //  1. Evaluates the original new T(){...} expression and stores the result in a variable.
        //  2. Registers handlers on the Property<T> objects
        //  3. Returns the new object (just as the original expression did)
        var statements = new List<Expression>();
        statements.Add(Expression.Assign(newObjectVar, init));
        statements.AddRange(handlerRegistrations);
        statements.Add(newObjectVar);

        return Expression.Block(
            new ParameterExpression[] { newObjectVar },
            statements);
    });

    return new BoundExpression<TResult>(
        (Expression<Func<TResult>>)modifiedExpression, table);
}

Motivating Example

In order to illustrate what we've achieved, I've created a silly little WPF window using the BoundControl control.  This is what the UI looks like when you run it:

The window has a few interactive features to illustrate some basic data binding.  Pretty boring stuff as far as UI's go, but all done from C# in a declarative, type-safe way.  The expression used to declare the UI (what would normally be done in XAML) looks like this:

C#
public MainWindow()
{
    InitializeComponent();

    var viewModel = new ViewModel()
    {
        Editable = { Value = false },
        Text = { Value = "asdf" }
    };

    grid.Children.Add(new BoundContent(() =>
        new DockPanel().Containing(
            new Button() {
                Content = viewModel.Editable ? 
                    (object)new Border() {
                        BorderThickness = new Thickness(5),
                        BorderBrush = Brushes.Blue,
                        Child = new TextBlock() {
                            Text = "Disable edit",
                            FontStyle = FontStyles.Italic
                        }
                    }
                    :
                    new TextBlock() {
                        Text = "Enable edit"
                    }
            }
            .DoWith(c => DockPanel.SetDock(c, Dock.Top))
            .OnClick(c => viewModel.Toggle()),

            new Button() {
                Content = "Reset text"
            }
            .DoWith(c => DockPanel.SetDock(c, Dock.Top))
            .OnClick(c => viewModel.ResetText()),

            new TextBlock() {
                Text = viewModel.Text.Value.Length + " characters"
            }
            .DoWith(c => DockPanel.SetDock(c, Dock.Top)),

            new TextBox() {
                Text = viewModel.Text,
                IsReadOnly = !viewModel.Editable,
                Background = viewModel.Editable ?
                    Brushes.LightBlue : 
                    Brushes.LightGreen
            }
            .OnTextChanged((s, args) => viewModel.Text.Set(s.Text))
        )));
}

There is a small amount of creativity used here in order to work around the fact that the current version of C# doesn't support assignments or statement blocks in Expression objects generated from lambda functions.  This is despite the fact that it supports them in normal lambdas, and the Expression class itself supports the underlying data structures.  Maybe we'll get this in a future version.  One example of such creativity is the DoWith extension method.  All this method does is pass the object to an Action delegate and return the same object.  This avoids having to store a temporary in order to call the DockPanel.SetDock method.

Notice how some of the properties are set using expressions, e.g.,:

C#
Background = viewModel.Editable ?
    Brushes.LightBlue : 
    Brushes.LightGreen

In order to achieve the same thing in XAML, you would need something like this:

XML
<Style TargetType="TextBox">
  <Setter Property="Background" Value="LightGreen"/>
  <Style.Triggers>
    <DataTrigger Binding="{Binding Editable}" Value="True">
      <Setter Property="Background" Value="LightBlue"/>
    </DataTrigger>
  </Style.Triggers>
</Style>

Much simpler, no?

Conclusions

This experiment really just barely scratches the surface of what is possible as well as what problems might be encountered.  The primary benefits of this approach that I can see are:

  • Binding expressions and property change notifications are type-safe
  • Binding expressions can be written as normal C# expressions with arbitrary complexity.

And here are some limitations as compared to standard WPF binding that I can see (I'm sure there are many more):

  • Two-way binding is not supported.  In this article, we assumed that UI-to-model data flow is done by subscribing to the control's events.  Another possibility is to try to encode the "binding direction" in the expression and handle it appropriately in the expression transform.
  • Collection change (i.e., INotifyCollectionChanged) is not supported.  This is a big one!  How should this be done in this context?
  • Control event handler (e.g., Button.Click) registration is more verbose.
  • Model or view model must exist before the UI is created and can't be changed.  In normal WPF code, you can set the control's DataContext at any time, and bindings get updated to use the new model.

If nothing else, my hope is that this article will get the reader's creative juices flowing about what can be accomplished using expression trees.  Happy metaprogramming!

License

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --