【C# WPF】TreeViewを使って異なる要素を表示する

タイトルの通りですが、異なる要素をTreeViewで表示させる必要があったので、メモとして残します。

開発環境

あまり関係なさそうですが、開発環境も残しておきます。

  • Visual studio 2019
  • .Net Framework4.6.1

完成品

以下の画像のように、地域(関東、関西)、都道府県、市区町村を表示させます。

 

手順

まず地域、都道府県、市区町村を表すクラスを定義します。

using System.Collections.Generic;

namespace WpfApp
{
    public interface ICategoryAndItem
    {
        string DispName { get; }

        IList<ICategoryAndItem> Children { get; }
    }
}
using System.Collections.Generic;

/// <summary>
/// 地域を表すクラス
/// </summary>
namespace WpfApp
{
    public class Region : ICategoryAndItem
    {
        public string DispName { get; private set; }

        /// <summary>
        /// 地域に紐づく都道府県一覧
        /// </summary>
        public IList<ICategoryAndItem> Children { get; private set; }


        public Region(string areaName)
        {
            DispName = areaName;
            Children = new List<ICategoryAndItem>();
        }

        public Pref AddAndGetPref(string prefName)
        {
            var res = new Pref(this, prefName);
            Children.Add(res);
            return res;
        }
    }
}
using System.Collections.Generic;

/// <summary>
/// 都道府県を表すクラス
/// </summary>
namespace WpfApp
{
    public class Pref : ICategoryAndItem
    {
        public Region Region { get; private set; }

        public string DispName { get; private set; }

        /// <summary>
        /// 都道府県に紐づく市区町村一覧
        /// </summary>
        public IList<ICategoryAndItem> Children { get; private set; }

        public Pref(Region region, string prefName)
        {
            Region = region;
            DispName = prefName;
            Children = new List<ICategoryAndItem>();
        }

        public Pref AddCityAndGetPref(string cityName)
        {
            Children.Add(new City(this, cityName));
            return this;
        }
    }
}
using System.Collections.Generic;
/// <summary>
/// 市区町村を表すクラス
/// </summary>
namespace WpfApp
{
    public class City : ICategoryAndItem
    {
        public Pref Pref { get; private set; }

        public string DispName { get; private set; }

        private readonly IList<ICategoryAndItem> EmptyList = new List<ICategoryAndItem>();

        public IList<ICategoryAndItem> Children
        {
            get
            {
                return EmptyList;
            }
        }
        public City(Pref pref, string cityName)
        {
            Pref = pref;
            DispName = cityName;
        }
    }
}

WPFの都合上、各クラスを同じものとして扱う必要があるため(*1)、IFを定義しています。

次に、View周りを定義します。(長くなってしまったので、以降一部を抜粋しています。)

MainWIndow.xaml

<Window x:Class="WpfApp.MainWindow"
        xmlns:local="clr-namespace:WpfApp">
    <Grid>
        <TreeView Name="TreeView">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="local:ICategoryAndItem" ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding DispName}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

MainWindow.xaml.cs

var kanto = new Region("関東");

kanto.AddAndGetPref("東京都")
     .AddCityAndGetPref("港区")
     .AddCityAndGetPref("文京区");

kanto.AddAndGetPref("千葉県")
     .AddCityAndGetPref("千葉市")
     .AddCityAndGetPref("木更津市");

var kansai = new Region("関西");

kansai.AddAndGetPref("大阪府")
      .AddCityAndGetPref("大阪市")
      .AddCityAndGetPref("福島区");

kansai.AddAndGetPref("兵庫県")
      .AddCityAndGetPref("神戸市")
      .AddCityAndGetPref("西宮市");

TreeView.ItemsSource = new List<ICategoryAndItem> { kanto, kansai };

MainWIndow.xamlでは、TreeViewの各要素(`Region`と`Children`の各要素)を`ICategoryAndItem`型として再帰的に表示させています。

また、市区町村は子要素を持たないので、空のListで返します。

以上。

参考

*1: WPF4.5入門 その26 「TreeViewコントロール その2」