The Binding you wanted from day one in WPF
Remember when you started playing with WPF? Remember when you coded your first binding?
{Binding ElementName=slider, Path=Value}
and then remember what you wanted to do next? Something like this I'm sure...
{Binding ElementName=slider, Path=Value/2}
(in case you missed it, I'm trying to divide Value by 2). At least I know I've always yearned for basic expression support in WPF bindings. Converters have always allowed you to do this but I've always found the process of creating a converter really interrupts my flow. I've since found ways to partly reduce the friction when using Converters but I'd be happier if I could just have support for expressions.
Long term readers of this blog will remember I'm a big fan of dynamic compilation and, particularly, Expressions which make Dynamic Compilation much easier. And so my idea for a Binding/Converter that supports expressions was born. And here it is:
<Slider x:Name="slider" />
<TextBlock Text="{Binding ElementName=slider, Path=Value, Converter={binding:ExpressionConverter Expression=x/2, InputType=sys:Double, Cache=true}}" />
Or, using the shorter syntax (assuming default values for InputType (double) and Cache (true):
<Slider x:Name="slider" />
<TextBlock Text="{Binding Value, ElementName=slider, Converter={binding:ExpressionConverter x/2}}" />
Nice! How does this work?
Shipping with the Visual Studio 2008 C# samples (available on MSDN Code Gallery) was a rather neat file called Dynamic.cs that contains a neat type called System.Linq.Dynamic.DynamicExpression (not to be confused with the DynamicExpression type that will ship with .NET 4.0).
This supports the parsing of LambdaExpressions as shown below:
ParameterExpression x = System.Linq.Expressions.Expression.Parameter(typeof(double), "x");
var exp = DynamicExpression.ParseLambda(
new ParameterExpression[] { x },
null,
"x/2");
var func = (Func<double,double>)exp.Compile();
Console.WriteLine(func(5));
Saweet. This little program would dump '2.5' to the Console window. Nice (I was about to start writing my own Exprssion parsing algorithm so thanks to my colleagues Zulfiqar Ahmed and James World for introducing me to this little gem).
This is the key piece of magic but you can download the source (below) to get a full view of the current implementation.
More examples
The reason the InputType defaults to double is because I imagine that the vast majority of times you'll want to use this for some basic layout magic (ActualWidth / 2 etc). However, there are many scenarios where you may wish to change the InputType. For example, imagine we have a type called Item with two DateTime properties, CreatedDate and ModifiedDate:
// TODO - for brevity I haven't implemented INotifyPropertyChanged
public class Item
{
public string Name { get; set; }
public DateTime CreateDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
And we might have a list of these items bound to an ItemsSource:
<ListBox ItemsSource="{Binding}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding CreatedDate, StringFormat='Created: {0:yyyy-MM-dd} '}" VerticalAlignment="Bottom" FontSize="10" />
<TextBlock Text="{Binding ModifiedDate, StringFormat='Modified: {0:yyyy-MM-dd} '}" VerticalAlignment="Bottom" FontSize="10" />
<TextBlock x:Name="NewText" Text=" NEW!" Foreground="Red" FontWeight="Bold" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And imagine we would like to highlight 'NEW' items, i.e. those where the ModifiedDate is equal to the CreatedDate and were created in the last week. Easy with the ExpressionConverter and a DataTrigger added to our DataTemplate:
<DataTrigger
Binding="{Binding Converter={binding:ExpressionConverter 'x.CreatedDate == x.ModifiedDate && Datetime.Now.AddDays(-7) < x.ModifiedDate', InputType={x:Type local:Item}}}"
Value="True">
<Setter TargetName="NewText" Property="UIElement.Visibility" Value="Visible" />
</DataTrigger>
Note, that in this case we have to specify the InputType. Also, we had to encode ampersand and less than in XML. So, did it work? Of course:
Performance
So, how does this stuff perform?
Well, not bad actually. The compilation of the expression is relatively slow but this only happens once per converter. Of course, experienced WPFers will know this could easily lead to terrible performance as, if within an DataTemplate used by an ItemsControl, the converter might be created hundreds of times, ouch!
This is why the ExpressionConverter has a Cache property which defaults to true. When Caching is on, compiled expressions are cached in a static dictionary and a matching expression (based on the Expression string and InputType) will always reuse this compiled expression. Note, if you have many different expressions. That is, the expressions are being generated at runtime, then you might leak memory and therefore may choose not to use the cache. Set the Cache property to false if you're worried about this - you will then incur a compilation per Converter. The solution here is to put the Converter high in the resource tree so it's only compiled once and used as a resource wherever needed:
<Application.Resources>
<binding:ExpressionConverter x:Key="SinConverter" Expression="Math.Sin(x)" Cache="false"/>
</Application.Resources>
Anyway, time for some rough figures about the speed of the expression itself. Here are the results for a million invocations of a reasonably complex Expression, "Math.Cos(x)/22+1/x":
The results you're interested in are the direct invoke and wrapped invoke. direct invoke (which took 269ms for a million invocations) is the same 'expression' as a raw function being invoked directly (note it included conversion to a double as required by an IValueConverter which takes an object parameter). The wrapped invoke represents the dynamically compiled expression also including the converstion to a double. This took 371ms for a million invocations. So it's slower, no suprise, but by a factor of about .3 in this test.
I should point out that, in some tests, the dynamic binding exhibited slower invocations of up to 4x but this really isn't the point. In most cases this performance would be more than adequate as the calculation of the expression (and the addition of the dynamic slowness) which totals a miniscule ~0.0004ms, clearly isn't going to be the bottleneck in your application's peformance. However, for performance sensitive scenarios where you might have thousands of ExpressionBindings, within an ItemsControl for example, you may want to skip this and go the old fashioned way and use a normal Binding and custom IValueConverter.
Download
Usual disclaimers apply and as always - your own mileage may vary.
Originally posted by Josh Twist on 7 January 2009 here.