C#のWPFでTreeViewへデータをバインドする

ツリー構造を展開したり折りたたんだりして表示できるTreeViewコントロール。

このTreeViewへデータをバインドするには、ItemTemplateへHierarchicalDataTemplateを定義して使う。


バインドさせるデータを格納するクラス

    public class TreeSource : INotifyPropertyChanged
    {
        private bool                             _IsExpanded = true;
        private string                           _Text       = "";
        private TreeSource                       _Parent     = null;
        private ObservableCollection<TreeSource> _Children   = null;


        public bool IsExpanded
        {
            get { return _IsExpanded; }
            set { _IsExpanded = value; OnPropertyChanged("IsExpanded"); }
        }

        public string Text
        {
            get { return _Text; }
            set { _Text = value; OnPropertyChanged("Text"); }
        }

        public TreeSource Parent
        {
            get { return _Parent; }
            set { _Parent = value; OnPropertyChanged("Parent"); }
        }

        public ObservableCollection<TreeSource> Children
        {
            get { return _Children; }
            set { _Children = value; OnPropertyChanged("Children"); }
        }



        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            if (null == this.PropertyChanged) return;
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        public void Add(TreeSource child)
        {
            if (null == Children) Children = new ObservableCollection<TreeSource>();
            child.Parent = this;
            Children.Add(child);
        }
    }

プロパティ変更が正しく通知されるようINotifyPropertyChangedインターフェースを実装。

 

IsExpandedプロパティ

展開されているか折りたたまれているかを格納。

 

Textプロパティ

表示する文字列。

 

Childrenプロパティ

子要素となるデータを格納する。

 

Parentプロパティ

親要素を格納する事で階層をさかのぼって参照出来るようにしている。

Parentプロパティを正しくセットする為、Addメソッドを作っている。(42~47行目)

 

 

 

 

 


XAML

<Window x:Class="WpfApplication1.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:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="250">
    <Grid Margin="20">
        <TreeView ItemsSource="{Binding TreeRoot}">
            <TreeView.Resources>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}"/>
                </Style>
            </TreeView.Resources>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type local:TreeSource}" ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Text}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

IsExpandedをバインド (12~14行目)

TreeViewItemのStyle定義を追加して、TreeViewItemのIsExpandedプロパティへTreeSourceのIsExpandedプロパティをバインドする。

 

HierarchicalDataTemplateを定義 (16から20行目)

TreeView.ItemTemplateをつかって、HierarchicalDataTemplateを定義する。

HierarchicalDataTemplateのDataTypeにデータの型をセット。

HierarchicalDataTemplateのItemsSourceには子要素となるChildrenプロパティをバインドする。

HierarchicalDataTemplate内にTextBlockを配置してTextプロパティを表示させる。

 

 

 

 

 


本体のソース

    public partial class MainWindow : Window
    {
        public ObservableCollection<TreeSource> TreeRoot { get; set; }


        public MainWindow()
        {
            InitializeComponent();

            TreeRoot = new ObservableCollection<TreeSource>();
            var item1  = new TreeSource() { Text = "Item1", IsExpanded = true };
            var item11 = new TreeSource() { Text = "Item1-1", IsExpanded = true };
            var item12 = new TreeSource() { Text = "Item1-2", IsExpanded = true };
            var item2  = new TreeSource() { Text = "Item2", IsExpanded = false };
            var item21 = new TreeSource() { Text = "Item2-1", IsExpanded = true };
            TreeRoot.Add(item1);
            TreeRoot.Add(item2);
            item1.Add(item11);
            item1.Add(item12);
            item2.Add(item21);

            DataContext = this;
        }
    }

 

 

コメントをお書きください

コメント: 9
  • #1

    yoshida (火曜日, 27 3月 2018 16:06)

    CheckTreeSourceの定義がありません。

  • #2

    yoshida (火曜日, 27 3月 2018 16:08)

    IsCheckedプロパティの定義もありません。ビルドできません。

  • #3

    AraramiStudio (火曜日, 27 3月 2018 17:50)

    yoshidaさん
    ご指摘ありがとうございます。

    TreeSourceクラス内の「CheckTreeSource」という表記は「TreeSource」の誤りでした。
    また、MainWindowクラス内に「IsChecked = true;」のような不要な記述についても誤りでした。

    以上の箇所について本文を修正いたしました。
    ご協力ありがとうございます。

  • #4

    Kato (金曜日, 14 1月 2022 14:55)

    参考にさせて頂き、TreeViewに表示させた階層構造の項目を選択した時の関数を追加しております。
    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

    この関数を実行した際、この関数内部に設定したローカル変数に選択した項目名を代入したいと考えております。

    初等的な質問で大変申し訳ありませんが、そのような方法はございますでしょうか?
    なお、e.NewValueの中のTextに項目名が返ってきていることは確認しております。


  • #5

    AraramiStudio (月曜日, 17 1月 2022 09:17)

    Katoさん
    コメントありがとうございます。

    SelectedItemChangedの引数 e の NewValue には選択された項目の値が入っています。
    e.NewValueは汎用的に扱えるよう object型になっているので TreeSource へキャストしてあげれば値を取ってくることができると思います。

    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
    TreeSource src = (TreeSource)e.NewValue;
    var text = src.Text;
    }

  • #6

    Kato (月曜日, 17 1月 2022 11:13)

    AraramiStudioさま
    コメントありがとうございました!!
    やりたいことができました!!

  • #7

    kawa (土曜日, 26 3月 2022 12:34)

    OnPropertyChanged("Childen")
    タイポです。
    コンストラクタ内では正常に動いているように見えますが、動的追加で子要素の再描画が実行されません。

  • #8

    AraramiStudio (水曜日, 30 3月 2022 10:49)

    kawaさん
    ご指摘ありがとうございます。

    該当の箇所について本文を修正いたしました。
    ご協力ありがとうございます。

  • #9

    S.M. (水曜日, 27 9月 2023 13:54)

    ObservableCollection<TreeSource> Children プロパティは setter をパブリックに公開しているので、外部から (variablename).Children.Add() または .Remove() を呼び出された場合、サイレントな変更になってしまうかと思われます。

    コンストラクタ等で、_Children.CollectionChanged イベントを購読し、 newItems と oldItems の変更を自クラスのプロパティ変更通知として通知する必要があります。