Fluent Silverlight – static reflection


Please view the table of content of this series for reference.

In my last post about binding dependency properties of a Silverlight control to a view model I used LINQ expressions to define the binding. I have written in the past about static reflection and the advantage of it versus dynamic reflection thus I do not want to repeat those arguments here. The main two reasons to use lambda expressions are

  • the binding can be defined in a strongly typed manner
  • the availability of meta information through the expression

When defining a data binding between a dependency property of a Silverlight control and a property on the view model we want to be able to use lambda expressions of the following type

  1. simple member (property or field on the view model) access, e.g. m => m.Description
  2. access of a nested member of of the view model, e.g. m => m.Name.Last
  3. any of the above but with a type casting, e.g. m => (decimal)m.NumberOfItems

there might be other types of expressions that make sense in certain scenarios but we found that in our applications we only need the three types mentioned above. Given one or the other of the above expressions we need to extract the name (or path) to the corresponding member (property or field). We need this information to define the binding. Remember that the Binding class expects this information as parameter in the constructor; that is to define a binding to the property of name Description of the data context of the view (which will be the view model in our case) we use the following code

var binding = new Binding("Description");
</p>

and to define a binding to the nested property Last of the data context we would have the following code

var binding = new Binding("Name.Last");
</p>

If we use the debugger we can easily see that the NodeType of the expression body is MemberAccess for the first two expression types described above whereas it is Convert for the third type of expression.

In any case we want to get the –>MemberExpression “embedded” in the lambda expression. In the former case this is just the body of the lambda expression, thus

var memberExpression = expression.Body as MemberExpression;
</p>

and in the latter case a type cast operation is a so called unary expression an thus we can get to the interesting part (the member expression) via the code below

var body = (UnaryExpression)expression.Body;
var memberExpression = body.Operand as MemberExpression;
</p>

once we have extracted the member expression from our lambda expression we can use it to get access to the PropertyInfo (or FieldInfo if the member is a field – but we only use properties for data binding) via

var propertyInfo = (PropertyInfo)memberExpression.Member;
</p>

and from the PropertyInfo we can get the name (or path) of the corresponding property (of the model)

var path = propertyInfo.Name;
</p>

Wait a second, now this is not so easy for the nested  property… there we effectively have a property chain. What we want is the path to the nested property to look like “Name.Last”. It turns out that we can just recursively iterate over a given member expression and extract the respective member to get a list of property info objects. This list of property info objects (in reverse order) can then be used to create the path.

Let’s first create this list of property info objects from the member expression</p> </p> </p>

var list = new List<PropertyInfo>();
 
while (memberExpression != null)
{
    list.Add((PropertyInfo)memberExpression.Member);
    memberExpression = memberExpression.Expression as MemberExpression;
}
</p>

and now from this list we create the path

var path = string.Empty;
foreach (var info in list.Reverse())
    path += path Length > 0 ? "." + info.Name : info.Name;
</p>

That’s it; quite easy yet powerful isn’t it? Below I give the full code I use in my samples. To facilitate the whole thing a bit I introduced a SingleProperty and a PropertyChain class (both implement the interface Accessor). Their respective code is also given below. First I use an extension method GetAccessor for our lambda expressions

public static class ExpressionExtensions
{
    public static Accessor GetAccessor<TModel>(this Expression<Func<TModel, object>> self)
    {
        var memberExpression = GetMemberExpression(self);
        return GetAccessor(memberExpression);
    }
 
    private static MemberExpression GetMemberExpression<TModel, T>(Expression<Func<TModel, T>> expression)
    {
        MemberExpression memberExpression = null;
        if (expression.Body.NodeType == ExpressionType.Convert)
        {
            var body = (UnaryExpression)expression.Body;
            memberExpression = body.Operand as MemberExpression;
        }
        else if (expression.Body.NodeType == ExpressionType.MemberAccess)
        {
            memberExpression = expression.Body as MemberExpression;
        }
 
        if (memberExpression == null) throw new ArgumentException("Not a member access", "expression");
        return memberExpression;
    }
 
    private static Accessor GetAccessor(MemberExpression memberExpression)
    {
        var list = new List<PropertyInfo>();
 
        while (memberExpression != null)
        {
            list.Add((PropertyInfo)memberExpression.Member);
            memberExpression = memberExpression.Expression as MemberExpression;
        }
 
        if (list.Count == 1)
        {
            return new SingleProperty(list[0]);
        }
 
        list.Reverse();
        return new PropertyChain(list.ToArray());
    }
}
</p>

and here is the definition for the Accessor interface

public interface Accessor
{
    string Name { get; }
}
</p>

and the code for the SingleProperty class

public class SingleProperty : Accessor
{
    private readonly PropertyInfo propertyInfo;
 
    public SingleProperty(PropertyInfo property)
    {
        propertyInfo = property;
    }
 
    public string Name
    {
        get { return propertyInfo.Name; }
    }
}
</p>

and for the PropertyChain classes

public class PropertyChain : Accessor
{
    private readonly PropertyInfo[] chain;
    private readonly SingleProperty innerProperty;
 
    public PropertyChain(PropertyInfo[] properties)
    {
        chain = new PropertyInfo[properties.Length - 1];
        for (var i = 0; i < chain.Length; i++)
        {
            chain[i] = properties[i];
        }
 
        innerProperty = new SingleProperty(properties[properties.Length - 1]);
    }
 
    public string Name
    {
        get
        {
            var returnValue = string.Empty;
            foreach (var info in chain)
                returnValue += returnValue.Length > 0 ? "." + info.Name : info.Name;
 
            returnValue += returnValue.Length > 0 ? "." + innerProperty.Name : innerProperty.Name;
 
            return returnValue;
        }
    }
}
</p>

Enjoy

Fluent Silverlight – Binding dependency properties to model properties