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

ExpressionTree Visualizer in WPF

Rate me:
Please Sign up or sign in to vote.
4.88/5 (8 votes)
24 May 2010CPOL4 min read 36.4K   397   19   5
A short project that displays a LINQ Expression tree in WPF.

Introduction

This article discusses an elegant way of displaying a LINQ Expression Tree in a WPF TreeView. It's a happy marriage between two hip ways of programming: functional LINQ and declarative WPF.

When playing around with LINQ Expressions, it's important to get a good grasp of what's going on. Visualizing the tree is a key tool in that understanding. Visual Studio 2010 has a Debug view (shown on the left) that displays the expression in a meta-language that's didn't quite work for me and hides the treelike structure of the beast. The standard quick-view in the debugger displays every property and subclass, this also distracted me from the essence.

The included code enables the user to navigate the essence of Expression Trees. It zooms in on those nodes in the tree that derive from System.Linq.Expressions.Expression. I believe it should be simple enough to change the implementation to display the trees in whatever way the user feels useful.

DebugView.jpg

WpfTreeview.jpg

Background

Expression Trees have been a part of .NET since 3.0. They've been getting some attention, but not by far the amount that is just. The SQL people among us are usually quite comfortable writing code that writes code. I use dynamic SQL quite frequently in my SQL code, and am able to do similar things with Expressions. Very powerful stuff.

This same trick of having a program write itself has been available to the Lisp family of programming languages, where you can can treat code as data and alter the program by manipulating that data. This family has not received a lot of love in the world, I believe mainly because of the Polish Notation. This type of Yoda speak, although arguably a less ambiguous notation than the way we were thought in school, just throws people off when first using these languages. Thrown off users do not add to the success of languages.

Now this trick has come to the .NET platform with LINQ Expressions. For brevity, this article only deals with displaying these trees in WPF. If you want to know more about this exciting new technology, I recommend this as a good starting point.

Using the code

Each node in an Expression Tree derives from an Expression. Usually, it has a few properties that contain other Expressions, thus forming a tree. There is no collection of children on each Expression node, so an adapter was written that uses Reflection to project all properties of type Expression into such a collection. Here's the listing of the ExpressionAdapter.

C#
// Adapter to ease binding to WPF. Main goal was to create
// an IEnumerable of childnodes that the TreeView can bind to
public class ExpressionAdapter { 
    
    // private fields. 
    private le.Expression _Expr; 
    private string _ParentPropertyName; 
    
    // constructor 
    public ExpressionAdapter(le.Expression Expr, string ParentPropertyName) { 
        _Expr = Expr; 
        _ParentPropertyName = ParentPropertyName; 
    } 
    
    // Returns string with information about the current Property - Expression pair 
    public string Text { 
        get { 
            return _ParentPropertyName + " = " + (_Expr == null ? 
               "null" : (_Expr.NodeType.ToString() + " : " + _Expr.ToString())); 
        } 
    } 
        
        // Returns all properties of type Expression as an IEnumerable<expressionadapter> 
        public IEnumerable<expressionadapter> Children { 
             get {
                if (_Expr == null) return null;
                else
                    return from childExpression in _Expr.GetType().GetProperties()
                           where childExpression.PropertyType == typeof(le.Expression)
                           select new ExpressionAdapter((le.Expression)
                           childExpression.GetValue(_Expr, null), childExpression.Name);
            }
        } 
    }

This adapter accepts a node of type Expression in the constructor and then exposes any properties of type Expression on that node as an IEnumerable called Chidlren. The WPF TreeView can be bound to that collection to fill the tree.

Below is the code that creates a simple expression and binds the tree to it.

C#
// create some bogus expression
Expression<func><int,>> expr = (x, y) =>
    x > 10 ?
        y > 20 ?
            100
            : x * (int)Math.Sqrt(x)
        : (int)((x + y) * Math.PI);

// wrap expression in ExpressionAdapter and then List<expressionadapter>
// (because a Treeview.ItemSource expects an IEnumerable) and bind TreeView to it
List<expressionadapter> l = new List<expressionadapter>() { 
             new ExpressionAdapter(expr, "Expression") };
tvExpression.ItemsSource = l;

And below is the XAML to let the TreeView bind to the ExpressionAdapter:

XML
<Window x:Class="ExpressionsWpf.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="WPF Expression Visualizer" Height="350" Width="525"
      xmlns:local="clr-namespace:ExpressionsWpf">
    <Grid>
        <TreeView Name="tvExpression" ItemsSource="={Binding}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="x:Type local:ExprWrapper" 
                             ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Text}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

Points of Interest

I was pleasantly surprised that an Expression Tree could be displayed like this with so little code. I like the declarative nature of the code where I only have to specify what needs to be done instead of writing loops to actually do it myself. Actually, I like declarative programming so much that when I see old-school messy code, with a lot of assignments and changes to state after the initial assignment, I feel my mind rejecting it. It's so easy to get those things wrong.

The nice part to notice in this code is that all this is done with lazy evaluation. The Children IEnumerable only gets enumerated when the user opens the node to actually see the children. This behaviour can be seen by putting a breakpoint on Children_get and then navigating the tree. When the expression becomes big or cyclic (not sure you can do that, I'll try and let you know), this is a very important advantage.

My first attempt was to bind the TreeView to the Expression with a IValueConverter and an Extension Method on Expession that exposed the children, but I switched to this approach because it looked cleaner and only needed one auxiliary class (one for the IValueConverter, and a static one for the extension method). In my coding guidelines, less is more.

I am still not completely satisfied with the constructor where I assign the Expression to a local variable. In functional programming, state should be on the call stack instead of the heap as much as possible. Here, all these ExpressionAdapters are the result of a function call to Children_get so they are on the stack, except for the top node. Also, the whole thing is immutable, so no problems could arise from it. The code works just fine the way it is, no need to get religious about it.

History

  • May 24, 2010: Version 1.0.
  • May 24, 2010: Version 1.1: Cleaned up formatting.
  • Mar 25, 2010: Version 1.2: Replaced the Children_get property with a cleaner LINQ query.

License

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


Written By
Leaseplan Corporation
Netherlands Netherlands
Gert-Jan is a Senior Quantitative Risk Manager at Leaseplan Corporation. In that job he doesn't get to code much he does these little projects to keep his skills up and feed the inner geek.

Comments and Discussions

 
Generalhave you seen this one too Pin
Sacha Barber24-May-10 6:09
Sacha Barber24-May-10 6:09 
GeneralRe: have you seen this one too Pin
gjvdkamp24-May-10 20:52
gjvdkamp24-May-10 20:52 
GeneralMade the article public and gave it a 5 to get you started Pin
Sacha Barber24-May-10 5:32
Sacha Barber24-May-10 5:32 
GeneralRe: Made the article public and gave it a 5 to get you started [modified] Pin
gjvdkamp24-May-10 5:43
gjvdkamp24-May-10 5:43 
GeneralRe: Made the article public and gave it a 5 to get you started Pin
Sacha Barber24-May-10 6:05
Sacha Barber24-May-10 6:05 

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.