Forwarding the DataGrid’s DataContext to its’ columns..


DataContext is a very handy inherited property on any WPF application..     
Most of the time, I set DataContext near the root on the [logical] tree, and let the inherited DataContext do its magic to bind the rest of the scene.

I recently tried to bind a DataGridColumn to its inherited DataContext (via its datagrid container) and got a very surprising answer on the output trace window:

“System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element…”

What is happening here?   
The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.

I needed the functionality so I had to create a workaround.. With out much thought I decided to: 

  1. Listen for DataContextChanged in the DataGrid
  2. When DataContext changes,  forward the new value to the DataGridColumns in the datagrid.
  3. Bind properties on the DataGridColumn to this ‘forwarded’ DataContext  ( as I originally intended)

To get it done,  I did not inherit from DataGrid and create a new class..  Instead i used the richness of WPF’s property system to pull a 1,2 punch:

  1. Override DataGrid’s DataContext metadata and listen for changes in it…
  2. Add a FrameworkElement.DataContextProperty to DataGridColumn …

Code looks like this:

FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));

FrameworkElement.DataContextProperty.OverrideMetadata ( typeof(DataGrid),            
new FrameworkPropertyMetadata
  
(null, FrameworkPropertyMetadataOptions.Inherits,
   new PropertyChangedCallback(OnDataContextChanged)));

The OnDataContextChanged callback simply forwards the DataContext from DataGrid to its columns:

public static void OnDataContextChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e)
{ 
    DataGrid grid = d as DataGrid ; 
    if ( grid != null  ) 
    {                 
        foreach ( DataGridColumn col in grid.Columns ) 
        { 
            col.SetValue ( FrameworkElement.DataContextProperty,  e.NewValue ); 
        } 
    } 
}


That is it. Now we are ready to databind to DataGridColumns.

<dg:DataGridTextColumn Binding="{BindingA}"

                   
Visibility="{Binding ElementName=ShowA,Path=IsChecked ,

                  
Converter={StaticResource BoolToVisConverter}}" />

 

You can download source code for a small sample from here.

DataGridColumnsAll
The project has 3 checkboxes that are databound to a viewmodel…    The DataGrid’s column Visibility is databound to this same viewmodel,  if the checkboxes are checked ,the respective column is visible.. if unchecked, it is collapsed..
DataGridColumnsnob

A few more thoughts on DataGridColumn not being in the tree ..

  1. Binding to other UIElements via ElementName will not work because there is no tree.  
  2. Binding to a  StaticResource works fine..
  3. Binding to x:Static  will work fine too.

     

Happy datagrid coding..  Again, there is source here.

Comments (4)

  1. marlongrech says:

    hey there,

    Josh also did something similar for this…

    http://blogs.infragistics.com/blogs/joshs/archive/2008/06/26/data-binding-the-isvisible-property-of-contextualtabgroup.aspx

    have a look it is really cool….

  2. Hey Marlon,

    I am a fan of Josh’s blog so I knew of the Freezable approach..     If I was to ‘come up’ with a few reasons I did not use it:

    1) I mostly wanted people to know the Columns have no DataContext … so I provided the smallest tightest solution keeping to show the problem and workaround..    

    2) Long-term simplicity..   I wanted to keep it all to code-behind and not touch the tree… this way when WPF team fixes the bug I can just blow away my static and be done..  not touching xaml or tree. Minimal surface (even if less flexibility).

    When I first wrote the post, I even wrote the line that said it could also be accomplished via Freezable.. not sure why I deleted and replaced it for a "with out much thought" prefix to the solution..  I blame mid-night blogging for that 😉

  3. Since the release of the WPF DataGrid there have been several common patterns of questions that developers