Binding Visibility for DataGridColumn in WPF
First of all, DataGridTextColumn
(or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid
. Hence, by default it doesn't inherit the DataContext
of the DataGrid
. However, it works for Binding
DP only and for no other DP's on DataGridColumn.
Since they don't lie in the same VisualTree, any attempt to get the DataContext
using RelativeSource
won't work as well because DataGridTextColumn
is unable to traverse up to the DataGrid
.
There are two other ways to achieve this though:
First using a Freezable
class. Freezable
objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.
First, create a 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 the DataGrid's DataContext and 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
. However, ElementName
works only in the same visual tree, whereas x:Reference doesn't have such constraints.
So, we can use that as well to our advantage. Create a dummy FrameworkElement
in XAML with Visibility set to collapsed
. The FrameworkElement will inherit the DataContext from its parent container, which can be a 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>
Bind DataGridTextColumn Visibility Property in WPF
The DataGridColumn
is not actually part of the VisualTree
, so bindings on the class cannot find their source
You can set things like the Visibility
and Width
property in the CellStyle
or HeaderStyle
of the DataGridColumn
, although that isn't quite the same.
The closest I've found to a solution would be to create a Freezable object in your <DataGrid.Resources>
that stores the binding, then use that StaticResource
in the Visibility
binding. It's not a pretty solution, but it's the only one I can find at this time.
You can view of an example of it here
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding IsChecked,
ElementName=IncludeFullHist,
Converter={StaticResource boolItemsConverter}}" />
</DataGrid.Resources>
<DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"
Visibility="{Binding Path=Data, Source={StaticResource proxy}}"/>
DataGridTextColumn Visibility Binding
The columns of a DataGrid
are abstract objects not appearing in the visual or logical tree. You cannot use ElementName
and RelativeSource
. Source
in combination with x:Reference
should work though:
Visibility="{Binding Source={x:Reference chkColumnVisible},
Path=IsChecked,
Converter={StaticResource BooleanToVisibilityConverter}}"
How do I bind the visibility property of a DataGridTextColumn using code in WPF?
Via BindingOperations
:
var column = new DataGridTextColumn();
BindingOperations.SetBinding(
column,
DataGridColumn.VisibilityProperty,
new Binding(...));
How can I bind visibility of a datagrid column on a custom wpf control?
Thank you all for your comments and input, and for taking a minute (I always appreciate any time you take!)
Here is the end result, what ended up working in case someone else runs in to this:
This post helped out a lot, but the syntax I needed was missing the relative source for TemplatedParent
:
(1) I am using a consumable control, and wanted to be able to set this visibility when the control is implemented. You can get to the ViewModel context using the steps in the post mentioned above.
(2) You need to put the binding Relative source to TemplatedParent
on either the proxy or the dummy element (that was the part I was missing).
... In a ControlTemplate:
<FrameworkElement x:Name="dummyElementToGetDataContext"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Visibility="Collapsed" />
<DataGrid>
<DataGrid.Columns>
......
<CheckBoxColumn Binding="{Binding SecondColumnStuff}"
Visibility="{Binding DataContext.ShouldDisplaySecondColumn,
Converter={StaticResource BooleanToVisibilityConverter},
Source={x:Reference dummyElementToGetDataContext}}"
.............
OR
Create the proxy and when declaring this as resource, set binding relative source to templated parent:
<DataGrid.Resources>
<controls:ControlProxy x:Key="ControlProxy" Control="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</DataGrid.Resources>
WPF set DataGridTextColumn Visibility based on another element
@Ruben solution worked. For anyone that cares here is the working code:
<StackPanel>
<StackPanel.Resources>
<FrameworkElement x:Key="ProxyElement"
DataContext="{Binding ElementName=MyTextBlock}"/>
</StackPanel.Resources>
<TextBlock Text="This is the text" Name="MyTextBlock" Margin="5">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}" >
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyTextBox, Path=Text}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBox Text="0" Name="MyTextBox" />
<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="H1" />
<DataGridTextColumn Header="H2" Visibility="{Binding DataContext.Visibility, Source={StaticResource ProxyElement}}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
Bind datagrid column visibility MVVM
DataGridColumn
s are not part of visual tree so they are not connected to the data context of the DataGrid
.
For them to connect together use proxy element approach like this...
Add a proxy
FrameworkElement
in your ancestor panel'sResources
.Host it into an invisible
ContentControl
bound to itsContent
.Use this
ProxyElement
asStaticResource
for data context source in your visibility binding.<StackPanel>
<StackPanel.Resources>
<local:BooleanToVisibilityConverter
x:Key="BooleanToVisibilityConverter" />
<FrameworkElement x:Key="ProxyElement"
DataContext="{Binding}"/>
</StackPanel.Resources>
<ContentControl Visibility="Collapsed"
Content="{StaticResource ProxyElement}"/>
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn
Visibility="{Binding DataContext.IsTextColumnVisibile,
Source={StaticResource ProxyElement},
Converter={StaticResource
BooleanToVisibilityConverter}}"
Binding="{Binding Text}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
Apart from DataGridColumn
, the above approach also works great to connect DataContext
to Popup
s and ContextMenu
s (i.e. any element that is not connected to the visual tree).
Silverlight Users
Sadly setting contents of content controls with any framework elements is not allowed in silverlight. So the workaround would be (this is just a guidance code for silverlight) ...
Change the framework element resource to something lightweight like a
Textblock
. (Silverlight does not allow specifying static resource ofFrameworkElement
type.)<StackPanel.Resources>
<TextBlock x:Key="MyTextBlock" />Write an attached property to hold text block against the content control.
<ContentControl Visibility="Collapsed"
local:MyAttachedBehavior.ProxyElement="{StaticResource MyTextBlock}" />In the attached dependency property changed event handler, set the bind the data context of the content control to the text block's.
private static void OnProxyElementPropertyChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
if (depObj is ContentControl && e.NewValue is TextBlock)
{
var binding = new Binding("DataContext");
binding.Source = depObj;
binding.Mode = OneWay;
BindingOperations.SetBinding(
(TextBlock)e.NewValue, TextBlock.DataContextProperty, binding);
}
}
So this way the textblock may not be connected to the visual tree but will probably be aware of the data context changes.
DataGridColumn Visibility with Converter
This should do the job
<DataGrid Margin="8" Style="{StaticResource CoDeDataGrid}" ItemsSource="{Binding Path=TableDataGridView}"
AutoGenerateColumns="False" IsReadOnly="True" Name="AxisGrid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.Coordinatesystem,
Converter={StaticResource EnumToDisplayTextConverter} , RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
...
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Style="{StaticResource CoDeButtonSmall}"
Command="{Binding Path=DataContext.OnSetReferenceCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding ElementName=AxisGrid, Path=SelectedItem}"
Visibility="{Binding Name, Converter={StaticResource ButtonNameToVisibilityConverter}, ConverterParameter={DataContext.TeachAxis}">
<Image Source="C:\Users\PA\Source\Repos\Source\Common.Resources\ImageResources\TestPicture.jpg" Height="24" Width="24"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Related Topics
Why am I Getting 'One or More Types Required to Compile a Dynamic Expression Cannot Be Found.'
Using Sse in C# Is It Possible
Set Dllimport Attribute Dynamically
Multicast Delegate of Type Func (With Return Value)
C#: Cast to Generic Interface with Base Type
Using Libtiff from C# (To Access Tiled Tiff Images)
MVC 5 Dynamic Rows with Begincollectionitem
How to Change Currentculture at Runtime
C# - How to Detect a Windows Shutdown/Logoff and Cancel That Action (After Asking the User)
How to Prevent Datagridview from Flickering When Scrolling Horizontally
Wpf Clickonce Dpi Awareness Per-Monitor V2
Draw Multiple Freehand Polyline or Curve Drawing - Adding Undo Feature
What's the Least Invasive Way to Read a Locked File in C# (Perhaps in Unsafe Mode)
SQL Injection Attack Prevention: Where Do I Start
Why Doesn't the C# Ternary Operator Work with Delegates