ざこノート
2018-06-13 [WPF]

[WPF] C#でDataGridColumnのHeaderとVisibilityをバインドする例

DataTableDataGridAutoGeneratingColumnで自動作成させたケースで、列のヘッダー名と非表示の設定をバインドで制御する例です。

環境

  • Windows 10 Pro 64bit 1709
  • Visual Studio Community 2017 15.7.3
  • .NET Framework 4.6.1
  • C# 7.0

結果

初期表示の状態

初期表示の状態

ボタンをクリック後

ボタンをクリック後

実装

このDictionaryを使用してバインドします。
KeyColumnNameです。

// ColumnHeaderの表示文言
public Dictionary<string, string> ColumnHeader { get; }
// Columnの表示/非表示
public Dictionary<string, BindableValue<Visibility>> ColumnVisibility { get; }

通常のDictionary<string, string>ではValueを更新した際に、明示的にINotifyPropertyChangedを通知してあげる必要があります。
Dictionary<string, BindableValue<T>>を使用する方法であれば通知は不要ですが、BindableValue<T>のインスタンスが必要になります。

BindableBase.cs

この基底クラスを使用しています。
[C#] INotifyPropertyChangedを楽に実装し、ReSharperのTemplateも作る

BindableValue.cs (Model)

namespace WpfDataGridStudy
{
    public class BindableValue<T> : BindableBase
    {
        public T Value
        {
            get => _value;
            set => SetProperty(ref _value, value);
        }
        private T _value;

        public BindableValue()
        {
        }

        public BindableValue(T value)
        {
            Value = Value;
        }
    }

    public static class BindableValue
    {
        public static BindableValue<T> Create<T>(T value) => new BindableValue<T>(value);
    }
}

ViewModel.cs (ViewModel)

using System.Collections.Generic;
using System.Data;
using System.Windows;

namespace WpfDataGridStudy
{
    public class ViewModel : BindableBase
    {
        public DataTable Table { get; } = new DataTable();
        public Dictionary<string, string> ColumnHeader { get; } = new Dictionary<string, string>();
        public Dictionary<string, BindableValue<Visibility>> ColumnVisibility { get; } = new Dictionary<string, BindableValue<Visibility>>();

        public ViewModel()
        {
            // DataTableなデータを取得したとする
            Table.Columns.Add("A");
            Table.Columns.Add("B");
            Table.Columns.Add("C");

            for (int i = 0; i < 10; i++)
            {
                Table.Rows.Add($"A-{i}", $"B-{i}", $"C-{i}");
            }

            // DataTableと同じColumnNameをキーとして作成
            foreach (DataColumn column in Table.Columns)
            {
                ColumnHeader.Add(column.ColumnName, $"{column.ColumnName}列ですよ");
                ColumnVisibility.Add(column.ColumnName, BindableValue.Create(Visibility.Visible));
            }
        }

        public void SetHeader()
        {
            ColumnHeader["A"] = "A列のヘッダー変更したよ";
            // A列しか変更していないが、Dictionary全体として通知する必要がある
            OnPropertyChanged(nameof(ColumnHeader));
        }

        public void SetVisible()
        {
            ColumnVisibility["B"].Value = Visibility.Collapsed;
        }
    }
}

MainWindow.xaml (View)

<Window x:Class="WpfDataGridStudy.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:WpfDataGridStudy"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        d:DataContext="{d:DesignInstance {x:Type local:ViewModel}}">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <StackPanel>
        <Button Content="バインドにより、A列ヘッダーを変更して、B列を非表示に変更します"
                Click="Button_OnClick"/>
        <DataGrid IsReadOnly="True"
                  ItemsSource="{Binding Table}"
                  AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs (Viewのコードビハインド)

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfDataGridStudy
{
    public partial class MainWindow : Window
    {
        private readonly ViewModel _viewModel;

        public MainWindow()
        {
            InitializeComponent();
            _viewModel = (ViewModel)DataContext;
        }

        private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            // Dictionary<string, string> ColumnHeader とバインド
            BindingOperations.SetBinding(e.Column,
                DataGridColumn.HeaderProperty,
                new Binding($"{nameof(ViewModel.ColumnHeader)}[{e.PropertyName}]")
                {
                    Source = _viewModel
                });

            // Dictionary<string, BindableValue<Visibility>> ColumnVisibility とバインド
            BindingOperations.SetBinding(e.Column,
                DataGridColumn.VisibilityProperty,
                new Binding($"{nameof(ViewModel.ColumnVisibility)}[{e.PropertyName}].{nameof(BindableValue<Visibility>.Value)}")
                {
                    Source = _viewModel
                });
        }

        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            _viewModel.SetHeader();
            _viewModel.SetVisible();
        }
    }
}

感謝