Binding to Properties of non-FrameworkElements

[Infragistics] Devin Rader / Friday, April 29, 2011

A common question we see from customers using our XAML controls is that they want to set a binding on a control property, but the property is exposed by an object that does not live in the Visual Tree.  Unfortunately in both Silverlight and WPF, objects must live in the Visual Tree in order also inherit the data context set above them (and also to participate in element binding), so its not always clear how to do this.  

In our controls, we may choose to make some objects not participate as part of the visual tree for several reasons, including:

  • The objects are not a visual part of the control and are simply used to organize and store properties.  As such they don't make sense as part of the visual tree.
  • Making them visual parts of the control to add it to the Visual Tree by, for example deriving from Framework Element, would make the object inherit nonsensical properties such as height, width and background.
  • Adding the object to the Visual Tree would come at a significant performance loss which in some scenarios is not acceptable.

In this post, I’ll look at two ways you can work around the data context inheritance problem.

Binding to StaticResource

The most obvious way to work around the issue of binding to properties of these control objects is to simply declare your ViewModel as a StaticResource in your View.  The sample below shows how I can bind the HeaderText property of a TextColumn in XamGrid by creating my ViewModel as a static resource.

<Grid x:Name="LayoutRoot" DataContext="{StaticResource vm}">
    <Grid.Resources>
        <vm:CustomersViewModel x:Key="vm" />
    </Grid.Resources>
    <ig:XamGrid>
        <ig:XamGrid.Columns>
            <ig:TextColumn Key="CustomerName" 
                 HeaderText="{Binding Source={StaticResource vm}, 
                 Path=Resources.CustomerNameHeaderText}" />
        </ig:XamGrid.Columns>
    </ig:XamGrid>
</Grid>

There are a number of drawbacks to using this approach, including the fact that it requires my ViewModel to have a default constructor.  Additionally, if this is being used inside of a container like a User Control, an instance of the View Model will be created for every instance of that container.

Binding to a Data Context Proxy

A second way to work around the binding problem is to create a Data Context proxy object.  A Data Context property object gives you a generic object that you can define as a StaticResource in your View so you can avoid having to directly define the ViewModel as a StaticResource.

Below you can see the code for the simple DataContextProxy object I created.  It exposes a single dependency property which lets me set and get an object:

public class DataContextProxy : DependencyObject 
{ 
    public object ProxiedObject 
    { 
        get { return (object)GetValue(ProxiedObjectProperty); } 
        set { SetValue(ProxiedObjectProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for ProxiedObject.  
    //   This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ProxiedObjectProperty = 
        DependencyProperty.Register("ProxiedObject", typeof(object), 
                                     typeof(DataContextProxy), null); 

}

Now if I want to bind the HeaderText property as I did in the previous section, I can simply set the Source of my binding to the ProxyObject:

<Grid x:Name="LayoutRoot">
    <Grid.Resources>
        <local:DataContextProxy 
            ProxiedObject="{Binding Path=DataContext,ElementName=LayoutRoot}" 
            x:Key="proxy" />
    </Grid.Resources>
    <ig:XamGrid>
        <ig:XamGrid.Columns>
            <ig:TextColumn Key="CustomerName" 
                 HeaderText="{Binding Source={StaticResource proxy}, 
                 Path=Resources.CustomerNameHeaderText}" />
        </ig:XamGrid.Columns>
    </ig:XamGrid>
</Grid>

Now I am free to set my ViewModel wherever I like since the DataContextProxy is simply referencing the context of the LayoutRoot Grid, who in turn may be inheriting its Data Context from some other element further up the Visual Tree.