Monday, August 15, 2011

Silverlight 5 Incompatibility: Play by the Rules

I've been using the Silverlight 5 beta for some time now with no issues, so it was a surprise when I came across a project that did not work. It was definitely a backwards compatibility issue because it would run fine in Silverlight 4, but once installed the version 5 runtime, no luck. I started digging deeper and found the culprit. The problem is easy to reproduce. Create a simple command:

public class SelectCommand : ICommand
    public bool CanExecute(object parameter)
        return true;

    public void Execute(object parameter)
        if (parameter != null)

    public event EventHandler CanExecuteChanged;

Then a view model (read-only so it doesn't have to implement property change notification):

public class ViewModel
    private ICommand _selectCommand;

    public List<string> Items
        get { return new List<string> {"one", "two", "three", "four"}; }

    public ICommand SelectedCommand
        get { return _selectCommand ?? (_selectCommand = new SelectCommand()); }

Now for the Xaml. Here is the rub ... in Silverlight 4, there is no such thing as ancestor binding to easily reference elements higher in the visual tree. It is a common pattern to have a view model with a collection bound to a list, and a command to operate against items in the list. The rub is that the data template for the items is scoped to the item itself, not the parent view model.

There are a number of ways to resolve this, such as using element name binding, but one hack was to stick a content control in the resource dictionary and use it to hold the parent data context. Take a look at this Xaml:

<Grid x:Name="LayoutRoot" Background="White">
        <ContentControl x:Key="BindingContent" Content="{Binding}"/>
    <ItemsControl ItemsSource="{Binding Items}">
                <TextBlock Text="{Binding}">
                        <i:EventTrigger EventName="MouseLeftButtonDown">
                            <i:InvokeCommandAction Command="{Binding Source={StaticResource BindingContent}, Path=Content.SelectedCommand}"

If you compile the example (put the grid in the main page and add the classes described earlier) and run it in Silverlight 4, it works no problem. The content control which is really designed to display UI is being hacked to hold the data context, and then the items can reference back to the control and use the content to bind to the command. When you click on an item in the list, you get a nice message box displaying the value.

This example is not so friendly when run in Silverlight 5. The clicks simply don't happen. You can even convert the project to a Silverlight 5 one, remove and re-add the reference to the System.Windows.Interactivity namespace, and it will still not register clicks. If you debug, you'll find that the trigger does fire. It is passed the command parameter with the value of the item in the list ... but the command is null, so there is nothing for it to call.

Why is that? What happened? If you know the history of Silverlight, since version 3.0 the team has made a monumental effort to keep the versions backwards-compatible. When going to Silverlight 5, you don't get a special Silverlight 4 runtime - you get a 5 runtime that supports all of the Silverlight 4 features. This is obviously a problem for this example because the code breaks.

I filed this as a bug for the Silverlight team to consider in case others have used this technique and need the compatibility, but the "true bug" is in the code itself. This is an example of a hack or an exploitation of a bug that allows something to happen, but isn't necessarily the intended behavior. For all I know the bug was fixed in Silverlight 5. If you dive into the Silverlight documentation for resource dictionaries, you'll find this little nugget:

In order for an object to be defined in and accessed from a ResourceDictionary, the object must be able to be shared ... Any object that is derived from the UIElement type is inherently not shareable, unless it is generated from a control template.

In a nutshell, a content control really doesn't belong in a resource dictionary. Actually, if you think about it, the entire thing is a bit of a hack because a content control is designed to be part of the visual tree and in this example, it's being used simply to marshall a data context.

A better approach would be to create a class that is specifically intended to hold onto the reference. That class is easy enough to create and can even keep the same Content property:

public class DataContextBinder : DependencyObject
    public static readonly DependencyProperty ContentProperty =
            new PropertyMetadata(null));

    public object Content
        get { return GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }

That is a simple class that is a dependency object (not a UIElement) and can hold any other type of object. It's not intended for the visual tree and is entirely shareable. And, sure enough, if you replace the content control in the previous example with the data context binder, the code runs fine in both Silverlight 4 and Silverlight 5.

Just another reason to take extra effort to play by the rules and learn what a control expects, so you don't suffer from a "hack" that gets called out in a later version.

Jeremy Likness


  1. Hi Jeremy,

    Thanks for your research. I have used the technique used in your example a lot and is good to know that it needs to be fixed. This saves a lot of time trying to find the problem in the code.

  2. But now the DataContextBinder instance will be shared, right? (In SL5, not SL4). So if you have two instances of your control, the first one will initialize DataContextBinder and bind, everything works fine. Now you create a second, independent, instance and this one will use the same instance of DataContextBinder as the first control. This will cause a huge bug in your system if SL5 runtimes are used.

  3. That's only true if you scope it to the application level, i.e. at the App.xaml level. If you scope it just to the grids/data sources that are using it, you will not have two instances of the control. That is the power of the rseource dictionary and its scopes. Again, I wouldn't recommend this method and would use ancestor binding in SL5, this is just a stop gap for forwards compatibility until SL5 is released.

  4. In that case the scoping may be flawed, because it does seem to share items placed in Grid.Resources of templated controls across instances.
    Have you explicitly tried this?

    Try creating a templated control with say an ICommand in Grid.Resources somewhere in its template. Make the ICommand a DependencyObject, define some dependency property and bind a value to it in the template using template binding. Then try referencing the command using StaticResource binding from an ItemTemplate.
    If I create such a SL4 app and run it with SL5 run-time and create two instance of the class, the second instance will use the same command instance as the first, i.e. my dependency property on the command has the value set in the first instance. This does not happen if using SL4 runtimes.

    Do you not experience the same problem? Perhaps the problem only occurs with templated controls, or ICommands?

  5. Yes, I have a project that does this throughout, has many different view models (dozens of them) and functions fine with the relative scoping. I am using a data template for the item template. Works fine. Not using it with a control template, which may be the difference. In that case you are right, it would share. Difference between defining it in the control template, vs. defining it in the resources of the container control in the Xaml.

  6. I believe the issue you are describing in this post is not really a bug. The DataTemplate is ment to describe what the data looks like, thus its DataContext is set to the data. By default, the DataContext of controls in a UserControl is set to the control, allowing developers to specify bindings without a source (the DataContext is the default source).

    The DataTemplate cannot have 2 DataContexts. Otherwise, if you specify a binding without a source, Silverlight wouldn't know which context to pick. If you want to bind to the UserControl (or TemplatedParent), you can set a helper object as the ItemsSource that has 2 properties, 1 for the data and 1 for the parent control. Then, in the DataTemplate, you can bind to either data or the parent control using the Path of the binding:

    public class Helper
    public object ParentControl { get; set; }
    public object Data { get; set; }

    I believe you can also specify an x:Name on the root tag and use the binding {Binding ElementName=MyRoot} to get the parent control.