Всем привет! На сегодняшнем уроке мы научимся выполнять привязку иерархических данных к нашим элементам управления.
Зачем? — Classes, Subclasses
Иерархический data binding бывает нужен в различных ситуациях, например для отображения структурированных данных (разбитых на более мелкие части, которые в свою очередь тоже разбиваются на подклассы)…
Как? — Templates, blabla
В WPF существует удобный способ — HierarchicalDataTemplate. Этот механизм используется как раз для подобных целей и мы им сегодня воспользуемся.
Где? — THE CORE
HierarchicalDataTemplate может применяться в таких контролах, как TreeView или ListBox. Собственно мы рассмотрим именно на примере первого.
Let’s the coding begin
Все как обычно, начинаем с модели aka Data Layer…
Вот наша модель, которую мы собираемся привязывать:
public class Country { public string Name { get; set; } public List<City> Cities { get; set; } } public class City { public string Name { get; set; } public List<Company> Companies { get; set; } } public class Company { public string Name { get; set; } public string Address { get; set; } public List<Employee> Employees { get; set; } } public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public string Title { get; set; } public int Age { get; set; } }
Кроме того, понадобится вот такой вот класс-фабрика, создающий фейковые иерархические данные (любые совпадения с реальностью случайны ;)):
public class ModelFactory { public List<Country> CreateSource() { return new List<Country> { new Country { Name = "Russia", Cities = new List<City> { #region City: MOSCOW new City { Name = "Moscow", Companies = new List<Company> { #region MOSCOW, Company 1 new Company { Name = "Y Trade Ltd.", Address = "Lenina St. 1", Employees = new List<Employee> { new Employee { Age = 30, FirstName = "Ivan", LastName = "Petrov", Title = "President" }, new Employee { Age = 28, FirstName = "Nikolay", LastName = "Vasilev", Title = "Project Manager" }, new Employee { Age = 27, FirstName = "Ilya", LastName = "Ileev", Title = "Architector" }, new Employee { Age = 42, FirstName = "Andrey", LastName = "Goncharov", Title = "Senior Java Developer" }, new Employee { Age = 22, FirstName = "Nikita", LastName = "Nikolaev", Title = "Senior .NET Developer" }, } }, #endregion #region MOSCOW, Company 2 new Company { Name = "Z-SOFT", Address = "Vrubelya 1", Employees = new List<Employee> { new Employee { Age = 45, FirstName = "Marina", LastName = "Svetikova", Title = "Director" }, new Employee { Age = 38, FirstName = "Nikolay", LastName = "Andreev", Title = "HR" }, new Employee { Age = 29, FirstName = "Sergey", LastName = "Sergeev", Title = ".NET Developer" }, new Employee { Age = 31, FirstName = "Svetlana", LastName = "Marinina", Title = "Junior .NET Developer" } } } #endregion } }, #endregion #region City: Omsk new City { Name = "Omsk", Companies = new List<Company> { #region OMSK, Company 1 new Company { Name = "IT-ProSoft Trading Consulting Ltd.", Employees = new List<Employee> { new Employee { Age = 27, FirstName = "Andrey", LastName = "Petrov", Title = "President" }, new Employee { Age = 35, FirstName = "Mikhail", LastName = "Danilov", Title = "HR" }, new Employee { Age = 29, FirstName = "Igor", LastName = "Samoilenko", Title = ".NET Trainer" }, new Employee { Age = 25, FirstName = "Alexander", LastName = "Sidorov", Title = ".NET Developer" } } } #endregion } } #endregion } } }; } }
Итак, модель у нас создается, самое время написать слой привязки модели к GUI, воспользуемся ObjectDataProvider и инстанцией класса ModelFactory. Вы увидите, что это очень легко осуществимо, благодаря XAML разметке:
Вот наш Window1.xaml:
<Window x:Class="WpfApplication1.Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="Window1" Height="300" Width="300"> <Window.Resources> <local:ModelFactory x:Key="ModelFactory"/> <ObjectDataProvider x:Key="ModelProvider" ObjectInstance="{StaticResource ModelFactory}" MethodName="CreateSource"/> <HierarchicalDataTemplate x:Key="theDataTemplate" ItemsSource="{Binding Path=Items}"> <TextBlock Text="{Binding Path=Name}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}" Margin="5,0,0,0"/> <TextBlock Text="{Binding Title}" Margin="5,0,0,0"/> <TextBlock Text="{Binding Age}" Margin="5,0,0,0"/> </StackPanel> </TextBlock> </HierarchicalDataTemplate> </Window.Resources> <Grid DataContext="{StaticResource ModelProvider}"> <TreeView ItemsSource="{Binding Source={StaticResource ModelProvider}}" ItemTemplate="{StaticResource theDataTemplate}"/> </Grid> </Window>
Код требует некоторого объяснения.
Сначала мы создаем экземпляр нашей модели (класс ModelFactory, описанный выше), он нужен для того, чтобы создать фейковые данные.
<local:ModelFactory x:Key="ModelFactory"/>
После того, как экземпляр создан, мы можем привязать данные к ObjectDataProvider:
<ObjectDataProvider x:Key="ModelProvider" ObjectInstance="{StaticResource ModelFactory}" MethodName="CreateSource"/>
ObjectDataProvider — очень удобная штука, которая позволяет нам не писать ни единой строчки кода в Window1.xaml.cs. Провайдер сам вызовет метод, получающий данные (CreateSource). Провайдер умеет получать данные, но не знает, что с ними делать дальше.
Для отображения данных мы выбрали TreeView контрол. Присваиваем данные в свойство ItemsSource ему вот таким образом:
<TreeView ItemsSource="{Binding Source={StaticResource ModelProvider}}" ItemTemplate="{StaticResource theDataTemplate}"/>
Итак, принцип иерархического шаблона таков: шаблон применяется к каждому экземпляру в списке данных (из ItemsSource), если встречаются совпадающие свойства (в секции Binding), то они отображаются, согласно описания.
Например: , свойство Name имеется у классов Country, City, Company, а значит у всех будет отображаться контрол TextBlock иерархически. Это же относится и к остальным свойствам.
Итак, вот что у нас получилось в итоге:
Всем спасибо, ждите интересных уроков по WPF!
Надеюсь вам понравился изложенный материал, так что не стесняемся комментировать и голосовать за него.