How can I hide a column in a WPF DataGrid
through a Binding?
This is what I did:
<DataGridTextColumn Header=\"Column header\"
Binding=\"{Binding ColumnValue}\"
Width=\"100\"
ElementStyle=\"{StaticResource DataGridRightAlign}\"
Visibility=\"{Binding MyColumnVisibility}\" />
And this is what I got (besides the column still visible):
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyColumnVisibility; DataItem=null; target element is \'DataGridTextColumn\' (HashCode=1460142); target property is \'Visibility\' (type \'Visibility\')
Don\'t know what governing means. Is there some Mr. President somewhere in my window that decides what works and what not? Or would I have to vote for something?
While searching the web for solutions, I found like a dozen pages with a promising title but totally unrelated or unreproducible contents. So this seems to be the first question about the problem. Any idea?
First of all DataGridTextColumn
or any other supported dataGrid columns doesn\'t lie in Visual tree of DataGrid
. Hence, by default it doesn\'t inherit DataContext
of DataGrid
. But, it works for Binding
DP only and for not other DP\'s on DataGridColumn.
Since, they doesn\'t lie in same VisualTree so any try to get DataContext using RelativeSource
won\'t work as well because DataGrid won\'t able to traverse up to DataGrid.
There are two ways to achieve that though:
First using Freezable
class - Freezable
objects can inherit the DataContext even when they’re not in the visual or logical tree. So, we can take advantage of that to our use.
First create class inheriting from Freezable
and Data
DP which we can use to bind in XAML:
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); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(\"Data\", typeof(object),
typeof(BindingProxy));
}
Now, add an instance of it in DataGrid resources so that it can inherit DataGrid\'s DataContext and then can bind with its Data DP:
<DataGrid>
<DataGrid.Resources>
<local:BindingProxy x:Key=\"proxy\" Data=\"{Binding}\"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Visibility=\"{Binding Data.MyColumnVisibility,
Source={StaticResource proxy}}\"/>
</DataGrid.Columns>
</DataGrid>
Second, you can refer to any UI element in XAML using ElementName
or x:Reference
. But ElementName
works only in same visual tree whereas x:Reference doesn\'t have such constraint.
So, we can use that as well to our advantage. Create dummy FrameworkElement
in XAML with Visibility set to collapsed. FrameworkElement will inherit DataContext from it\'s parent container which can be Window or UserControl.
And can use that in DataGrid:
<FrameworkElement x:Name=\"dummyElement\" Visibility=\"Collapsed\"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header=\"Test\"
Binding=\"{Binding Name}\"
Visibility=\"{Binding DataContext.IsEnable,
Source={x:Reference dummyElement}}\"/>
</DataGrid.Columns>
</DataGrid>
<Window.Resources>
<ResourceDictionary>
<FrameworkElement x:Key=\"ProxyElement\" DataContext=\"{Binding}\" />
</ResourceDictionary>
</Window.Resources>
<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content=\"{StaticResource ProxyElement}\" Visibility=\"Collapsed\" />
<mch:MCHDataGrid Height=\"350\"
AutoGenerateColumns=\"False\"
FlowDirection=\"LeftToRight\"
ItemsSource=\"{Binding PayStructures}\"
SelectedItem=\"{Binding SelectedItem}\">
<DataGrid.Columns>
<DataGridTemplateColumn Width=\"70\"
Header=\"name\"
IsReadOnly=\"True\"
Visibility=\"{Binding DataContext.IsShowName,
Source={StaticResource ProxyElement}}\">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text=\"{Binding FieldName}\" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</mch:MCHDataGrid>
Sample of bound property in view model:
private Visibility _isShowName;
public Visibility IsShowName
{
get { return _isShowName; }
set
{
_isShowName = value;
OnPropertyChanged();
}
}