Saturday, November 22, 2008 12:40 AM
jaimer
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:
- Listen for DataContextChanged in the DataGrid
- When DataContext changes, forward the new value to the DataGridColumns in the datagrid.
- 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:
- Override DataGrid’s DataContext metadata and listen for changes in it…
- 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.
 |
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.. |
|
A few more thoughts on DataGridColumn not being in the tree ..
- Binding to other UIElements via ElementName will not work because there is no tree.
- Binding to a StaticResource works fine..
- Binding to x:Static will work fine too.
Happy datagrid coding.. Again, there is source here.