Данный пример написан как ответ на вопрос заданный на формуме MSDN.
Под катом показано, как при помощи паттерна "декоратор" отобразить в виде дерева то, что первоначально назвать деревом можно с очень большой натяжкой.
Итак, есть некая структура классов вот такого вида:
public class Project
{
public string Name { get; set; }
public Types types { get; set; }
public Instances instances { get; set; }
}
public class Types
{
public string Name { get; set; }
public List<ProjectType>; projectType { get; set; }
}
public class ProjectType
{
public string Name { get; set; }
}
public class Instances
{
public string Name { get; set; }
public List<ProjectInstance> projectInstance { get; set; }
}
public class ProjectInstance
{
public string Name { get; set; }
}
Исходнная вводная заключается в том, что структуру классов менять нельзя, а получить желательно вот такое дерево:
Несмотря на то, что WPF может для разных способов как показать дерево, для данного случая применить их напрямую затруднительно, т.к. в классе Project нет одной дочерней коллекции, а есть два класса из которых еще их коллекции надо извлечь.
Именно для таких случаев нам и может пригодиться паттерн "декоратор". Мы, рассширим функционал наших классов, но не за счет насследования, а за счет включения.
Для решения поставленной задачи воспользуемся вот таким классом "декоратором":
public object Item { get; set; }
get
{
Type t = Item.GetType();
PropertyInfo pi = t.GetProperty("Name");
return pi.GetValue(Item, null).ToString();
}
}
get
{
List<Wrapper> list = new List<Wrapper>();
Как вам не знаю, а по мне так просто супер. И главное кроме "декоратора" вообще ничего сложного нет.
P.s. Пока писал извлечение Name в декораторе через рефлекшен, придумал как вообще обойтись без этого свойтсва. Кто скажет как?
P.p.s. Если в TreeView воспользоваться DataTemplateSelector-ом, котрый в зависимсоти от типа Item-а будет подсовывать разные DataTemplate, то можно и украшательством заняться.
Под катом показано, как при помощи паттерна "декоратор" отобразить в виде дерева то, что первоначально назвать деревом можно с очень большой натяжкой.
Итак, есть некая структура классов вот такого вида:
public class Project
{
public string Name { get; set; }
public Types types { get; set; }
public Instances instances { get; set; }
}
public class Types
{
public string Name { get; set; }
public List<ProjectType>; projectType { get; set; }
}
public class ProjectType
{
public string Name { get; set; }
}
public class Instances
{
public string Name { get; set; }
public List<ProjectInstance> projectInstance { get; set; }
}
public class ProjectInstance
{
public string Name { get; set; }
}
Исходнная вводная заключается в том, что структуру классов менять нельзя, а получить желательно вот такое дерево:
Несмотря на то, что WPF может для разных способов как показать дерево, для данного случая применить их напрямую затруднительно, т.к. в классе Project нет одной дочерней коллекции, а есть два класса из которых еще их коллекции надо извлечь.
Именно для таких случаев нам и может пригодиться паттерн "декоратор". Мы, рассширим функционал наших классов, но не за счет насследования, а за счет включения.
Для решения поставленной задачи воспользуемся вот таким классом "декоратором":
public
class Wrapper
{public object Item { get; set; }
public string Name
{get
{
Type t = Item.GetType();
PropertyInfo pi = t.GetProperty("Name");
return pi.GetValue(Item, null).ToString();
}
}
public List<Wrapper>
Children
{get
{
List<Wrapper> list = new List<Wrapper>();
if
(Item is Project)
{
list.Add(
new
Wrapper() { Item = (Item as Project).types
}
);
list.Add(
new
Wrapper() { Item = (Item as Project).instances
}
);
}
if
(Item is Types)
{
foreach
(var item in
(Item as Types).projectType)
{
list.Add(
new
Wrapper() { Item = item }
);
}
}
if
(Item is Instances)
{
foreach
(var item in
(Item as Instances).projectInstance)
{
list.Add(
new
Wrapper() { Item = item }
);
}
}
return list;
}
}
}
На форму, кидаем TreeView, у которого прописываем тривиальный DataTemplate:
<Window x:Class="TreeViewExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView x:Name="tvProjects">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock
Text="{Binding Name}"
/>
HierarchicalDataTemplate>
TreeView.ItemTemplate>
TreeView>
Grid>
Window>
Готовим тестовые данные, подсовываем их в TreeView:
public
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += new
RoutedEventHandler(MainWindow_Loaded);
}
void
MainWindow_Loaded(object sender, RoutedEventArgs e)
{
List<Project> projects = new
List<Project>();
for (int i = 0; i < 2; i++)
{
Project
pt = new Project()
{ Name = "Project " + (i + 1) };
pt.types = new
Types() { Name = "Types",
projectType = new List<ProjectType>() };
for
(int j = 0; j < 3; j++)
{
pt.types.projectType.Add(new ProjectType()
{ Name = "Type" + (j + 1) });
}
pt.instances = new Instances() {
Name = "Instances",
projectInstance = new List<ProjectInstance>() };
for
(int j = 0; j < 3; j++)
{
pt.instances.projectInstance.Add(new ProjectInstance() { Name = "Instances" + (j + 1) });
}
projects.Add(pt);
}
List<Wrapper> wrappers = new
List<Wrapper>();
foreach
(var item in
projects)
{
wrappers.Add(new Wrapper() {
Item = item });
}
tvProjects.ItemsSource = wrappers;
}
}
Смотрим на результат:Как вам не знаю, а по мне так просто супер. И главное кроме "декоратора" вообще ничего сложного нет.
P.s. Пока писал извлечение Name в декораторе через рефлекшен, придумал как вообще обойтись без этого свойтсва. Кто скажет как?
P.p.s. Если в TreeView воспользоваться DataTemplateSelector-ом, котрый в зависимсоти от типа Item-а будет подсовывать разные DataTemplate, то можно и украшательством заняться.
Комментариев нет:
Отправить комментарий