Иерархические данные и HierarchicalDataTemplate

Последнее обновление: 10.08.2016

В отличие от простых списковых элементов типа ListBox или ComboBox, элементы TreeView и Menu способны отображать иерархические данные, построенные по образу дерева. Как у дерева могут быть ветви, у которых, в свою очередь, также могут быть ветви, так и у TreeView могут быть узлы высшего уровня, которые могут содержать подузлы, а в подузлах также могут храниться подузлы.

Для работы именно с иерархическими данными в WPF имеется специальный тип шаблонов данных - HierarchicalDataTemplate. Этот шаблон задает формат отображения уровня данных. Рассмотрим на примере.

Пусть у нас есть следующий класс:

public class Node
{
    public string Name { get; set; }
    public ObservableCollection<Node> Nodes { get; set; }
}

Этот класс представляет иерархические данные - объект Node может содержать несколько других объектов Node.

Теперь в коде xaml определим элемент TreeView для отображения данных:

<Window x:Class="DataApp.DataWindow"
        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:DataApp"
        mc:Ignorable="d"
        Title="DataWindow" Height="300" Width="300">
    <Grid>
        <TreeView x:Name="treeView1">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

С помощью свойства ItemTemplate у TreeView задается шаблон отображения для каждого объекта Node в виде HierarchicalDataTemplate.

Атрибут ItemsSource="{Binding Path=Nodes}" осуществляет привязку к внутренней коллекции Nodes, которая имеется в объекте Node.

Далее элемент <TextBlock Text="{Binding Name}" /> устанавливает привязку к свойству Name объекта Node.

Теперь в файле кода окна определим коллекцию объектов Node, которая будет отображаться в TreeView:

public partial class DataWindow : Window
{
    ObservableCollection<Node> nodes;
    public DataWindow()
    {
        InitializeComponent();

        nodes = new ObservableCollection<Node>
        {
            new Node
            {
                Name ="Европа",
                Nodes = new ObservableCollection<Node>
                {
                    new Node {Name="Германия" },
                    new Node {Name="Франция" },
                    new Node
                    {
                        Name ="Великобритания",
                        Nodes = new ObservableCollection<Node>
                        {
                            new Node {Name="Англия" },
                            new Node {Name="Шотландия" },
                            new Node {Name="Уэльс" },
                            new Node {Name="Сев. Ирландия" },
                        }
                    }
                }
            },
            new Node
            {
                Name ="Азия",
                Nodes = new ObservableCollection<Node>
                {
                    new Node {Name="Китай" },
                    new Node {Name="Япония" },
                    new Node { Name ="Индия" }
                }
            },
            new Node { Name="Африка" },
            new Node { Name="Америка" },
            new Node { Name="Австралия" }
        };
        treeView1.ItemsSource = nodes;
    }
}

Здесь просто создается список, который привязывается к TreeView. В итоге у нас получится следующее отображение:

HierarchicalDataTemplate в WPF

Рассмотрим другой пример. Пусть объекты узлой будут отличаться по типу. Для этого определим следующие классы:

public class Company
{
    public string Name { get; set; }
    public ObservableCollection<Smartphone> Phones { get; set; }
    public Company()
    {
        Phones = new ObservableCollection<Smartphone>();
    }
}
public class Smartphone
{
    public string Title { get; set; }
}

Компания-производитель содержит список произведенных моделей телефонов.

Теперь изменим код TreeView в xaml:

<TreeView x:Name="treeView1">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Phones}">
            <TextBlock Text="{Binding Name}" />
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title}" />
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Теперь для отображения внутренних данных, то есть объектов Phone задается дополнительный шаблон:

<HierarchicalDataTemplate.ItemTemplate>
    <DataTemplate>
        <TextBlock Text="{Binding Title}" />
    </DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>

Опять же в коде C# можно создать и установить источник данных для TreeView:

ObservableCollection<Company> companies = new ObservableCollection<Company>()
{
    new Company
    {
        Name = "Samsung",
        Phones = new ObservableCollection<Smartphone>
        {
            new Smartphone {Title = "Galaxy Note 7" },
            new Smartphone {Title = "Galaxy S 7" }
        }
    },
    new Company
    {
        Name = "Apple",
        Phones = new ObservableCollection<Smartphone>
        {
            new Smartphone { Title="iPhone 7" },
            new Smartphone { Title="iPhone 6S"}
        }
    },
    new Company
    {
        Name="Xiaomi",
        Phones = new ObservableCollection<Smartphone>
        {
            new Smartphone {Title="Redmi Note 2" },
            new Smartphone {Title="Mi5" }
        }
    }
};
treeView1.ItemsSource = companies;

В итоге получится следующее приложение:

Иерархические данные для TreeView в WPF

Аналогичным образом можно определить шаблон для элемента Menu:

<Menu x:Name="mainMenu" VerticalAlignment="Top" Height="30">
    <Menu.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Phones}">
            <TextBlock Text="{Binding Name}" />
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title}" />
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </Menu.ItemTemplate>
</Menu>
HierarchicalDataTemplate в меню в WPF

Получение данных из xml

Для представления иерархических данных очень удобен формат xml. И в WPF мы можем легко связн данные из xml с иерархическим элементом - TreeView или Menu. Допусти, у нас есть в проекте следующий файл nodes.xml:

<?xml version="1.0" encoding="utf-8" ?>
<nodes>
  <node title="Европа">
    <node title="Германия" />
    <node title="Франция" />
    <node title="Великобритания">
      <node title="Англия" />
      <node title="Шотландия" />
      <node title="Уэльс" />
      <node title="Сев. Ирландия" />
    </node>
  </node>
  <node title="Азия">
    <node title="Китай" />
    <node title="Япония" />
    <node title="Индия" />
  </node>
</nodes>

С помощью XmlDataProvider мы можем подключить этот файл к TreeView:

<Window x:Class="DataApp.DataWindow"
        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:DataApp"
        mc:Ignorable="d"
        Title="DataWindow" Height="300" Width="300">
    <Window.Resources>
        <XmlDataProvider x:Key="nodesProvider" Source="nodes.xml" XPath="nodes/node" />
    </Window.Resources>
    <Grid>
        
        <TreeView ItemsSource="{Binding Source={StaticResource nodesProvider}}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding XPath=node}">
                    <TextBlock Text="{Binding XPath=@title}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850