We all fased with problem when we need to hide column of DataGrid by some condition. There are at least two approaches to solve this problem. Bouth of approaches requires proxy element. I use bouth of these approaches. As you see, using of FreezableProxy
as proxy element does't require ContentControl
, but requires specify additional class (FreezableProxy
). Using of FrameworkElement
as proxy does't require specify additional class (like FreezableProxy
), but requires adding ContentControl
to markup.
XAML:
<Window.Resources>
<SampleRadioBoxCheckedConverter:FreezableProxy x:Key="FreezableProxy" Data="{Binding}"/>
<FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"/>
<DataGridTextColumn Header="Type"
Visibility="{Binding Data.IsPartnerColumnVisible, Source={StaticResource FreezableProxy}}"/>
<DataGridTextColumn Header="Number"
Visibility="{Binding DataContext.IsPartnerColumnVisible, Source={StaticResource FrameworkElement}}"/>
</DataGrid.Columns>
</DataGrid>
<ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>
</Grid>
Code-behind:
public class FreezableProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new FreezableProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(FreezableProxy));
}
public partial class MainWindow : INotifyPropertyChanged
{
private Visibility _isPartnerColumnVisible = Visibility.Hidden;
public Visibility IsPartnerColumnVisible
{
get
{
return _isPartnerColumnVisible;
}
set
{
_isPartnerColumnVisible = value;
RaisePropertyChanged("IsPartnerColumnVisible");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(String prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
Bouth approaches look very similiar. Suggest we use second approach (using FrameworkElement
). Then I have a question - why I need to specify additional ContentControl
if Binding
actually set to {StaticResource FrameworkElement}
? What a magic is ContentControl
doing? When we use FrezableProxy
we need not specify any ContentControl-s
or something else and it works fine.
UPDATE
<FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
/////<ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>
Why this doesn't work? ContentControl is commented but I set DataContext
property of FrameworkElement
explicitly.
Issue is DataGridColumns doesn't lies in same Visual tree as that of its parent dataGrid. Hence, binding doesn't work because DataContext is not inherited can can't be found using RelativeSource because it depends on Visual Tree.
So, all the approaches mentioned are to pass on DataContext to columns.
Now, to difference between two approaches:
FreezableProxy approach:
Actual source reference for this is here. And magic behind Freezable is defined here, so quoting from the same link:
The primary purpose of Freezable 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.
So, you don't need any ContentControl or any proxy control because with freezable in place we get inherited DataContext automatically.
FrameworkElement + ContentControl approach:
DataContext property is declared with flag FrameworkMetadataOptions.Inherits
. What it means that child control inherit it automatically from it's parent element unless set explicitly for child control.
So ContentControl inherit DataContext automatically from Grid and child element of ContentControl will inherit it from ContentControl.That's why FrameworkElement inherit it from ContentControl. (No need to bind DataContext manually). This will work:
<FrameworkElement x:Key="FrameworkElement"/>
Another approach is to use x:Reference
as described here in my answer.