The DataContext property in WPF is extremely handy, because it is automatically inherited by all children of the element where you assign it; therefore you don’t need to set it again on each element you want to bind. However, in some cases the DataContext is not accessible: it happens for elements that are not part of the visual or logical tree. It can be very difficult then to bind a property on those elements…
Let’s illustrate with a simple example: we want to display a list of products in a DataGrid. In the grid, we want to be able to show or hide the Price column, based on the value of a ShowPrice property exposed by the ViewModel. The obvious approach is to bind the Visibility of the column to the ShowPrice property:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding ShowPrice,
Converter={StaticResource visibilityConverter}}"/>
Unfortunately, changing the value of ShowPrice has no effect, and the column is always visible… why? If we look at the Output window in Visual Studio, we notice the following line:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’)
The message is rather cryptic, but the meaning is actually quite simple: WPF doesn’t know which FrameworkElement to use to get the DataContext, because the column doesn’t belong to the visual or logical tree of the DataGrid.
We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding DataContext.ShowPrice,
Converter={StaticResource visibilityConverter},
RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
Or we can add a CheckBox bound to ShowPrice, and try to bind the column visibility to the IsChecked property by specifying the element name:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding IsChecked,
Converter={StaticResource visibilityConverter},
ElementName=chkShowPrice}"/>
But none of these workarounds seems to work, we always get the same result…
At this point, it seems that the only viable approach would be to change the column visibility in code-behind, which we usually prefer to avoid when using the MVVM pattern… But I’m not going to give up so soon, at least not while there are other options to consider
The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree. I don’t know the exact mechanism that enables this behavior, but we’re going to take advantage of it to make our binding work…
The idea is to create a class (I called it BindingProxy for reasons that should become obvious very soon) that inherits Freezable and declares a Data dependency property:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
We can then declare an instance of this class in the resources of the DataGrid, and bind the Data property to the current DataContext:
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
The last step is to specify this BindingProxy object (easily accessible with StaticResource) as the Source for the binding:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding Data.ShowPrice,
Converter={StaticResource visibilityConverter},
Source={StaticResource proxy}}"/>
Note that the binding path has been prefixed with “Data”, since the path is now relative to the BindingProxy object.
The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.

(21 votes) 



Amazing, it worked like a charm!
I was stucked with this problem for a while…thank you very much!
But suppose if i want to bind the Text property to some property in my DataContext and i want it to be two way binding. But with this StaticResource we can only bind it OneWay from Source to target. How two way binding can be achieved?
You can make the binding two-way, it shouldn”t be an issue… have you tried it?
Oh i was trying to set it over the proxy binding. My bad. Thanks a lot for such a nice article..!!
I was stuck for hours trying to get an Infragistics XamDataTree to behave nicely in a MVVM setting. The “nodes” in the tree behave just like the DatagridTextColumn in your example. I gave that BindingProxy a try and it worked right away. That saves me from having to write a lot of ugly code to manage the different context menus for each node types (and probably break MVVM a little in the process.) You”re a life saver! Very clever trick. Merci beaucoup!
Thanks,
Michel
Wow – very elegant solution! I”ve been banging my head for hours with this problem, and none of the other solutions I found really solved it in XAML. This is a MUST for my MVVM project, and it works beautifully!!
Thanks for posting this!
Robin
Thanks for the article, it saved me a lot of work because i was thinking to create one column at a time on the fly. And that was not looking good. However, I am stuck with a small problem with this solution, my .NET part of application works with this binding proxy but my silverlight application does not recognize Freezable obviously because its not using .NET framework. Do you have any idea who can I achieve same binding in a silverlight xaml view?
Sorry, I don”t have any experience with Silverlight, so I don”t know how to tackle this problem in SL…
This was a very useful post and it helps me lot in my work. Now we are porting our application to WinRT, while doing this i have found that the Freezable class was not found in WinRT. so please suggest a way to overcome this.
Thanks,
Sankar
Hi Sankar, I haven”t really got my hands on WinRT yet, so I don”t have an answer for that… sorry.
Thanks so much! This is exactly what I was looking for.
Thanks a lot!
Thanks a lot, this really caused a lot of trouble till i found your solution
Great solution for this cases
Awesome, Thank you
I think this works too:
DataContext=”{Binding RelativeSource={RelativeSource AncestorType=GridViewColumnHeader}, Path=DataContext}” Binding=”{Binding Price}”
No it doesn”t, because RelativeSource uses the visual tree, and the DataGridColumn is not part of the visual tree…
Is there any difference while using this trick with Silverlight?
Also I am using older version of .NET and cannot find Freezable class. Any idea?
Thanks !!
Hi Ket,
The .NET framework itself has the Freezable class since the first version of WPF (.NET 3.0). However Silverlight only uses a subset of the .NET framework, and this class isn”t part of it. I don”t know if there”s another way to make it work in Silverlight…
thank you very much!
Hi,
It was very nice, but what i need is bind width property and when i resize a column in grid then i want to update the property in viewmodel…its not working..can you help….?
Hi Arena,
What”s causing you trouble exactly? You just need to do a two-way binding on your ViewModel property (Mode=TwoWay)
I had tried that but actual problem is when i resize column width on UI by mouse then width property in viewmodel is not getting updated…i dont know why…i am new to WPF…so please help me….
Hi Arena,
I don”t know why it isn”t working for you, but it doesn”t seem related to my blog post in any way… You should probably ask your question on Stack Overflow or some other forum.
Ok…thank you Thomas…:)