C#
WPF
VisualStudio

【WPF】DataGridでツリー表示を実現する

More than 1 year has passed since last update.

概要

 WPF(C#)でツールを作成するにあたって、ツリー表示させる必要が出てきたため、そこで作成したサンプルコードを載せます。環境は以下の通りです。

  • Visual Studio 2017
  • NugetにPrism.Mvvmをインストール済み

 以下はツールのキャプチャ画像であり、行追加ボタンを押して生成した"天財女王"の左にあるトグルボタンで子階層の表示・非表示を切り替えます。
DataGridのツリー表示.png

ソースコード

MainWindow.xaml
<Window x:Class="DataGridTest.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:DataGridTest"
        mc:Ignorable="d"
        Title="MainWindow" x:Name="WindowDataGrid" Height="350" Width="525">

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

    <StackPanel>
        <Button Content="行追加" Click="Button_Click"/>
        <DataGrid Name="dataGrid" CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding Data}">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ToggleButton Visibility="{Binding VisibleGroup}" IsChecked="{Binding IsOpen, UpdateSourceTrigger=PropertyChanged}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="名前" Binding="{Binding Name}" />
                <DataGridTextColumn Header="年齢" Binding="{Binding Age}" />
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;

namespace DataGridTest
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // 行追加ボタン押下処理
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var vm = this.DataContext as ViewModel;
            if (null == vm) { return; }

            // ツリー表示となるデータを追加する
            vm.Add(new Person(vm._dumpData, vm.Data) { Name = "天財女王", Age = 57, VisibleGroup = Visibility.Visible });
        }
    }
}

Person.cs
using Microsoft.Practices.Prism.Mvvm;
using System.Collections.ObjectModel;
using System.Windows;

namespace DataGridTest
{
    // DataGridに表示するデータ
    public class Person : BindableBase
    {
        private ObservableCollection<Person> _Child;
        private ObservableCollection<Person> _Parent;

        // 名前
        public string Name { get; set; }

        // 年齢
        private int _Age;
        public int Age
        {
            get => _Age;
            set
            {
                if (value < 0)
                {
                    MessageBox.Show("範囲外の値が設定されました。");
                    OnPropertyChanged("Age");
                }
                else
                {
                    SetProperty(ref _Age, value);
                }
            }
        }

        // 階層を展開するためのボタンの表示状態
        public Visibility VisibleGroup { get; set; } = Visibility.Collapsed;

        // 子階層を表示しているか否か
        private bool _IsOpen = false;
        public bool IsOpen
        {
            get => _IsOpen;
            set
            {
                _IsOpen = value;
                if (null != _Child)
                {
                    // 子階層があれば、表示/非表示に応じて、データを挿入/削除する
                    if (_IsOpen)
                    {
                        int index = _Parent.IndexOf(this) + 1;
                        foreach (Person item in _Child)
                        {
                            _Parent.Insert(index, item);
                        }
                    }
                    else
                    {
                        int index = _Parent.IndexOf(this) + 1;
                        foreach (Person item in _Child)
                        {
                            _Parent.RemoveAt(index);
                        }
                    }
                }
            }
        }

        public Person() { }

        // 子階層と親階層を設定する
        public Person(ObservableCollection<Person> child, ObservableCollection<Person> parent)
        {
            _Child = child;
            _Parent = parent;
        }
    }
}
ViewModel.cs
using Microsoft.Practices.Prism.Mvvm;
using System.Collections.ObjectModel;
using System.Windows;

namespace DataGridTest
{
    public class ViewModel : BindableBase
    {
        // 子階層用のデータ
        public ObservableCollection<Person> _dumpData = new ObservableCollection<Person>
        {
            new Person{Name=" 天財息子", Age=29},
            new Person{Name=" 天財娘", Age=27}
        };

        // 初期データ
        private ObservableCollection<Person> _Data = new ObservableCollection<Person>
        {
            new Person{Name="天財太郎", Age=19},
            new Person{Name="天財次郎", Age=17}
        };

        public ObservableCollection<Person> Data
        {
            get => _Data;
            set
            {
                SetProperty(ref _Data, value);
            }
        }

        public void Add(Person data)
        {
            Data.Add(data);
            Data[0].VisibleGroup = Visibility.Hidden;
        }
    }
}