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}}">

    
    
    <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}">

    
    
    <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}}">

        
        
        <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

Newest Articles