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

Jaime Rodriguez
On Windows Phone, Windows Presentation Foundation, Silverlight and Windows 7

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

Rate This
  • Comments 4

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.

Page 1 of 1 (4 items)