В отличие от простых списковых элементов типа 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. В итоге у нас получится следующее отображение:
Рассмотрим другой пример. Пусть объекты узлой будут отличаться по типу. Для этого определим следующие классы:
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;
В итоге получится следующее приложение:
Аналогичным образом можно определить шаблон для элемента 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>
Для представления иерархических данных очень удобен формат 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>