WPF TreeView HierarchicalDataTemplate - привязка к объекту с несколькими дочерними коллекциями - PullRequest
52 голосов
/ 16 декабря 2009

Я пытаюсь заставить TreeView связать мою коллекцию, чтобы все группы отображали вложенные группы, а каждая группа отображала запись.

Как я могу использовать HierarchicalDataTemplate, чтобы TreeView обрабатывал как подгруппы, так и коллекции записей?

Группы показывают подгруппы и записи:

Example:
Group1
--Entry
--Entry
Group2
--Group4
----Group1
------Entry
------Entry
----Entry
----Entry
--Entry
--Entry
Group3
--Entry
--Entry


Объекты:

<ч />
namespace TaskManager.Domain
{
    public class Entry
    {
        public int Key { get; set; }
        public string Name { get; set; }
    }
}

namespace TaskManager.Domain
{
    public class Group
    {
        public int Key { get; set; }
        public string Name { get; set; }

        public IList<Group> SubGroups { get; set; }
        public IList<Entry> Entries { get; set; }
    }
}

Данные испытаний:

<ч />
namespace DrillDownView
{
    public class TestData
    {

        public IList<Group> Groups = new List<Group>();

        public void Load()
        {
            Group grp1 = new Group() { Key = 1, Name = "Group 1", SubGroups = new List<Group>(), Entries = new List<Entry>() };
            Group grp2 = new Group() { Key = 2, Name = "Group 2", SubGroups = new List<Group>(), Entries = new List<Entry>() };
            Group grp3 = new Group() { Key = 3, Name = "Group 3", SubGroups = new List<Group>(), Entries = new List<Entry>() };
            Group grp4 = new Group() { Key = 4, Name = "Group 4", SubGroups = new List<Group>(), Entries = new List<Entry>() };

            //grp1
            grp1.Entries.Add(new Entry() { Key=1, Name="Entry number 1" });
            grp1.Entries.Add(new Entry() { Key=2, Name="Entry number 2" });
            grp1.Entries.Add(new Entry() { Key=3,Name="Entry number 3" });

            //grp2
            grp2.Entries.Add(new Entry(){ Key=4, Name = "Entry number 4"});
            grp2.Entries.Add(new Entry(){ Key=5, Name = "Entry number 5"});
            grp2.Entries.Add(new Entry(){ Key=6, Name = "Entry number 6"});

            //grp3
            grp3.Entries.Add(new Entry(){ Key=7, Name = "Entry number 7"});
            grp3.Entries.Add(new Entry(){ Key=8, Name = "Entry number 8"});
            grp3.Entries.Add(new Entry(){ Key=9, Name = "Entry number 9"});

            //grp4
            grp4.Entries.Add(new Entry(){ Key=10, Name = "Entry number 10"});
            grp4.Entries.Add(new Entry(){ Key=11, Name = "Entry number 11"});
            grp4.Entries.Add(new Entry(){ Key=12, Name = "Entry number 12"});

            grp4.SubGroups.Add(grp1);
            grp2.SubGroups.Add(grp4);

            Groups.Add(grp1);
            Groups.Add(grp2);
            Groups.Add(grp3);
        }
    }
}

XAML:

<ч />
<Window x:Class="DrillDownView.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TaskManager.Domain;assembly=TaskManager.Domain"
        Title="Window2" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type local:Group}" ItemsSource="{Binding SubGroups}">
                    <TextBlock Text="{Binding Path=Name}" />
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="{x:Type local:Entry}" ItemsSource="{Binding Entries}">
                    <TextBlock Text="{Binding Path=Name}" />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

XAML.CS:

<ч />
public partial class Window2 : Window
{
    public Window2()
    {
        InitializeComponent();
        LoadView();
    }

    private void LoadView()
    {
        TestData data = new TestData();
        data.Load();
        GroupView.ItemsSource = data.Groups;
    }
}

Ответы [ 4 ]

98 голосов
/ 16 декабря 2009

A HierarchicalDataTemplate - это способ сказать «это то, как вы визуализируете объект этого типа, а вот свойство, которое можно проверить, чтобы найти дочерние узлы под этим объектом»

Следовательно, вам нужно единственное свойство, которое возвращает «потомки» этого узла. например (Если вы не можете сделать и Group, и Entry производными от общего типа Node)

public class Group{ ....
        public IList<object> Items
        {
            get
            {
                IList<object> childNodes = new List<object>();
                foreach (var group in this.SubGroups)
                    childNodes.Add(group);
                foreach (var entry in this.Entries)
                    childNodes.Add(entry);

                return childNodes;
            }
        }

Далее вам не нужно HierDataTemplate для входа, так как у записи нет детей. Таким образом, XAML необходимо изменить, чтобы использовать новое свойство Items и DataTemplate для Entry:

<TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:Group}" ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type local:Entry}" >
            <TextBlock Text="{Binding Path=Name}" />
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

А вот как это выглядит. Screenshot of Output

11 голосов
/ 16 декабря 2009

Я думаю, что вы большую часть пути туда ... с небольшим количеством переделки вы должны заставить это работать довольно легко ...

Я бы предложил вам создать базовый абстрактный класс (или интерфейс, в зависимости от того, что вы предпочитаете) и унаследовать / реализовать его для класса Group и Entry ...

Таким образом, вы можете выставить свойство в объекте вашей группы

public ObservableCollection<ITreeViewItem> Children { get; set; }

^ на этом этапе вы можете принять решение, если он заменит ваши списки подгрупп и записей, или просто сложит их вместе и вернет их в свойство getter ...

Теперь все, что вам нужно, это заполнить коллекцию Children объектами Group или Entry, и HierarchicalDataTemplate будет правильно отображаться при размещении объектов в TreeView.

Еще одна заключительная мысль: если Entry всегда является «нижним уровнем» дерева (то есть не имеет дочерних элементов), тогда вам не нужно определять HierarchicalDataTemplate для объекта Entry, достаточно DataTemplate.

Надеюсь, это поможет:)

9 голосов
/ 04 августа 2011

Вот альтернативная реализация ответа Гишу, которая возвращает IEnumerable вместо IList и использует ключевое слово yield для упрощения кода:

public class Group
{
    ...

    public IEnumerable<object> Items
    {
        get
        {
            foreach (var group in this.SubGroups)
                yield return group;
            foreach (var entry in this.Entries)
                yield return entry;
        }
    }
}
3 голосов
/ 07 марта 2012

Этот пост помог мне при поиске решения для той же проблемы: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

с использованием MultiBinding и CompositeCollectionConverter ..

/ С уважением, Андерс

...