Row Header losing DataContext?

Oct 29, 2008 at 8:49 PM
I created a simple test application that used the DataGrid with row headers that were set through data templates and binding.  However, the row headers were always blank.  I finally went and downloaded Snoop [http://blois.us/Snoop/]  and analyzed the application.  It turns out that the DataContext on my TextBlock was empty.  The Parent, a ContentPresenter, also didn't have a DataContext, but the Parent of that, a StackPanel, did.  So, I was able to solve my problem by writing some fancy data binding that went up through the parents to get the DataContext.

I guess the main question I have is:  Why is the TextBlock losing the DataContext?  Shouldn't all children controls have the same DataContext as their Parent by default?

For those interested, here is the XAML code for the app (Is there a way to put it in a codeblock?):
<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:Linq="clr-namespace:System.Xml.Linq;assembly=System.Xml.Linq"
    Title="Window1" Height="300" Width="300">
    
    <toolkit:DataGrid  HeadersVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding Elements}" SelectionUnit="Cell">
        
        <!-- Column definitions for the data grid -->
        <toolkit:DataGrid.Columns>
            <toolkit:DataGridTextColumn Header="Name" Binding="{Binding Name}" CanUserSort="False" IsReadOnly="True"/>
            <toolkit:DataGridTextColumn Header="Value" Binding="{Binding Value}"/>
            <toolkit:DataGridTextColumn Header="Parent" Binding="{Binding Parent}" IsReadOnly="True"/>
        </toolkit:DataGrid.Columns>
        
        <!-- Template for displaying the row headers. -->
        <toolkit:DataGrid.RowHeaderTemplate>
            <DataTemplate DataType="{x:Type Linq:XElement}">
                <!-- The TextBlock is somehow losing the DataContext, so we have to go back to the parent controls and acquire the DataContext. -->
                <!-- Find the DataContext by hardcoding the path back through the TemplatedParents.  (First parent is ContentProvider, Second is StackPanel) -->
                <TextBlock Text="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneWay, Path=TemplatedParent.TemplatedParent.DataContext.Name}" />
            </DataTemplate>
        </toolkit:DataGrid.RowHeaderTemplate>
    </toolkit:DataGrid>
</Window>

The original line I had that didn't work, but should have, was:
<TextBlock Text="{Binding Mode=OneWay, Path=Name}" />

Here is the C# code that constructs the XElement list that the DataGrid is bound to:
public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            DataContext = this;

            for (int i = 0; i < 10; i++)
            {
                XElement newElement = new XElement("Element" + i);
                newElement.Value = "Value" + i;
                Elements.Add(newElement);
            }
        }

        private List<XElement> elements = new List<XElement>();

        public List<XElement> Elements
        {
            get { return elements; }
            set { elements = value; }
        }
    }