DataContext in a Custom Control

In WPF or Silverlight, new developers will often stumble across the concept of data context when learning about data binding. Data context is a mechanism that allows a framework element to define a data source for itself and its children to use when they participate in data binding. For example, a group box containing a number of associated data fields might specify its data context as some model containing a property for each of those fields. Each child control of that group box only needs to specify a relative binding path containing the name of the property it would like to bind to. Linking to a model object containing that property isn’t needed because that model was already set as the data context on a parent element and is inherited.

When building a custom control, a very common technique is set that control’s data context back to itself so that child controls can be bound to properties in that same control’s code-behind.

<UserControl x:Class="MyControl" 
    DataContext="{Binding RelativeSource={RelativeSource self}}">

    <!-- Only a relative path is needed because data -->
    <!-- context was set at a higher level           -->
    <TextBlock Text="{Binding Title}" />

</UserControl>

Did you see the bug in the code above? It’s not immediately apparent and takes a little experience to realize that a user control should never specify its own data context in its definition. But why?

The answer becomes more apparent when we try to combine the data context concept with our new control elsewhere.

<Grid DataContext="{StaticResource ViewModel}">

    <!-- Here we'd expect this control to be bound to -->
    <!-- MyContent on our ViewModel resource          -->
    <my:MyControl Content="{Binding MyContent}" />

</Grid>

In the above example, we’d expect MyControl to behave like any other framework element and bind its Content to MyContent on our ViewModel resource. Unfortunately, for anyone trying to use this control, it’s actually binding its Content property to MyContent on itself (which probably doesn’t even exist). The reason is that we already hard-coded a data context into the control’s definition, which will take precedent in this case.

Fortunately, there’s an easy way to solve this problem. Instead of specifying our data context on the root of the control itself, we should specify the data context on the control’s top-level child element. This is often a Grid called LayoutRoot that Visual Studio generates automatically.

<UserControl x:Class="MyControl">

    <Grid x:Name="LayoutRoot"
        DataContext="{Binding RelativeSource={RelativeSource self}}">

        <!-- This child element will still bind the -->
        <!-- same way!                              -->
        <TextBlock Text="{Binding Title}" />

    </Grid>

</UserControl>

Framework elements within the control itself can bind using a relative path in the same way, and data context in classes using the control will not be polluted, which will help prevent unexpected side effects.

Posted on November 26, 2010 from Calgary

About

My name is Brandur. I'm a polyglot software engineer and part-time designer working at Heroku in San Francisco, California. I'm a Canadian expat. My name is Icelandic. Drop me a line at brandur@mutelight.org.

Aside from technology, I'm interested in energy and how it relates to our society, travel, longboarding, muay thai, symphonic metal, and the guitar.

If you liked this article, consider finding me on Twitter.