Binding the visibility of DataGridColumn in WPF / MVVM

See this StackOverflow answer for the original inspiration.

Notable quote: “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.”

Here is an example of how to achieve this in a Visual Studio WPF application.

Step 1: Create a Visual Studio WPF application:

Step 2: Create a 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”.

BindingProxy.cs

using System.Windows;

namespace DataGridColumnVisibility
{
   public class BindingProxy : Freezable
   {
      public static readonly DependencyProperty DataProperty =
         DependencyProperty.Register("Data", typeof(object),
            typeof(BindingProxy));

      public object Data
      {
         get { return GetValue(DataProperty); }
         set { SetValue(DataProperty, value); }
      }

      #region Overrides of Freezable

      protected override Freezable CreateInstanceCore()
      {
         return new BindingProxy();
      }

      #endregion
   }
}

Step 3: Set the styles and converters needed in the App.xaml file

App.xaml

<Application x:Class="DataGridColumnVisibility.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"            
             StartupUri="MainWindow.xaml">
    
    <Application.Resources>
        <Style x:Key="ColumnHeaderTextStyle" TargetType="DataGridColumnHeader">
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Foreground" Value="Black" />
        </Style>

        <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        
    </Application.Resources>
</Application>

Step 4: Create the Main Window ViewModel class

This class we use to create and store the items that will be displayed in the DataGrid control.

MainWindowViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;

namespace DataGridColumnVisibility
{
   public class Item
   {
      public Item(string value1, string value2, string value3)
      {
         Value1 = value1;
         Value2 = value2;
         Value3 = value3;
      }

      public string Value1 { get; set; }
      public string Value2 { get; set; }
      public string Value3 { get; set; }
   }

   public class MainWindowViewModel : INotifyPropertyChanged
   {
      private bool _firstColumnChecked;
      private List<Item> _items;

      public MainWindowViewModel()
      {
         _items = new List<Item>
         {
            new Item("A", "B", "C"),
            new Item("X", "Y", "Z"),
            new Item("1", "2", "3"),
            new Item("000", "001", "010")
         };
      }

      public List<Item> Items
      {
         get { return _items; }
         set
         {
            _items = value;
            OnPropertyChanged(nameof(Items));
         }
      }

      public bool FirstColumnChecked
      {
         get { return _firstColumnChecked; }
         set
         {
            _firstColumnChecked = value;
            OnPropertyChanged(nameof(FirstColumnChecked));
         }
      }

      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string propertyName)
      {
         VerifyPropertyName(propertyName);
         var handler = PropertyChanged;
         handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }

      [Conditional("DEBUG")]
      private void VerifyPropertyName(string propertyName)
      {
         if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            throw new ArgumentNullException(GetType().Name + " does not contain property: " + propertyName);
      }
   }
}

Step 5: Set the view in MainWindow.xaml

MainWindow.xaml

<Window x:Class="DataGridColumnVisibility.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridColumnVisibility"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
            <RowDefinition Height="10" />
        </Grid.RowDefinitions>

        <StackPanel
            Margin="10,0,10,0"
            VerticalAlignment="Center"          
            Orientation="Horizontal">
            <CheckBox
                Margin="0,0,20,0"
                FontWeight="SemiBold"
                IsChecked="{Binding Path=FirstColumnChecked, Mode=TwoWay}"
                Content="View Column 1" />
        </StackPanel>

        <DataGrid
            Grid.Row="1"
            FontSize="13"            
            Margin="10,0,10,0"
            AutoGenerateColumns="False"
            CanUserAddRows="False"
            RowHeaderWidth="30"
            ColumnHeaderHeight="45"            
            ColumnHeaderStyle="{StaticResource ColumnHeaderTextStyle}"
                     
            ItemsSource="{Binding Items}">

            <DataGrid.Resources>
                <local:BindingProxy x:Key="FirstColumnCheckedProxy" Data="{Binding FirstColumnChecked}"   />
            </DataGrid.Resources>

            <DataGrid.Columns>
                <DataGridTextColumn                      
                    Header="Column 1"   
                    Width="70"                    
                    Binding="{Binding Value1}"
                    Visibility="{Binding .Data, Converter={StaticResource BoolToVisibilityConverter}, Source={StaticResource FirstColumnCheckedProxy}}">
                </DataGridTextColumn>

                <DataGridTextColumn                    
                    Header="Column 2" 
                    Width="70"
                    Binding="{Binding Value2}" >
                </DataGridTextColumn>

                <DataGridTextColumn 
                    Header="Column 3" 
                    Width="70"
                    Binding="{Binding Value3}">
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Observe that when the program is first run the FirstColumnChecked is initially defaulted to false, thereby causing the first column not to be shown:

And when the Checkbox is set, the first column is displayed:

Leave a Reply