Binding DataGrid columns to DataContext items
The WPF DataGrid is a fantastic component. It’s flexible and can serve in a lot of different scenarios. Recently I was using the DataGrid in a project and had the need to dynamically hide certain columns via a users selection in a configuration section.
The idea is the user would check a box to hide a corresponding column represented in the DataGrid and the column would not appear.
I implemented the XAML as below, binding the columns visibility flag to the checkboxes IsChecked value with the appropriate converter. I was surprised to find that this didn’t work at all. Toggling the checkbox did nothing.
Looking in the IDE’s Output window I noticed the following binding error message:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsChecked; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=19699911); target property is ‘Visibility’ (type ‘Visibility’)
After some digging around, I found that the DataGrid columns aren’t actually in the Visual Tree of the window as you can see from the screenshot below.
This is the source of our error, as the DataGridTextColumn we are trying to bind to things isn’t participating in the Visual Tree. To work around this we can implement the DataGrid’s DataContextChanged event and “forward’ the DataContext to the individual columns.
This would allow the individual columns to be able to be bound to items within the DataContext. Below is a quick and dirty implementation of how to perform this forwarding.
A more elegant way to do this would be to override the DataContextChanged property on all Datarid’s using a metadata override.
Here’s the code behind to handle the DataContextChanged event and forward the context to the columns. Notice that I’ve also implemented a property with change notification to be used by the checkbox to store it’s “IsChecked” value so that it can be retrieved via the DataContext.
And here’s the modified XAML showing the Checkbox now bound to the new property and the DataGrid’s first column bound to the same value using the DataContext.
Because the DataGridTextColumn is not part of the visual tree I don’t believe there is any way to perform direct element bindings.
If someone figures this out, drop me a note.