Datagridtextcolumn Visibility Binding

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

DataGridColumns 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...

  1. Add a proxy FrameworkElement in your ancestor panel's Resources.

  2. Host it into an invisible ContentControl bound to its Content.

  3. Use this ProxyElement as StaticResource 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 Popups and ContextMenus (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) ...

  1. Change the framework element resource to something lightweight like a Textblock. (Silverlight does not allow specifying static resource of FrameworkElement type.)

     <StackPanel.Resources>
    <TextBlock x:Key="MyTextBlock" />
  2. Write an attached property to hold text block against the content control.

     <ContentControl Visibility="Collapsed" 
    local:MyAttachedBehavior.ProxyElement="{StaticResource MyTextBlock}" />
  3. 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



Leave a reply



Submit