65 votes

WPF TreeView HierarchicalDataTemplate - liaison à un objet avec plusieurs collections enfant

Je suis en train d'essayer d'obtenir un TreeView de lier ma collection de sorte que tous les groupes imbriqués groupes et chaque groupe d'entrée.

Comment puis-je utiliser l' HierarchicalDataTemplate , de sorte que l' TreeView traitera à la fois des sous-groupes et les Entrées de collection?

Les groupes montrent des sous-groupes et les inscriptions:

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


Objets:


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; }
    }
}

Données de Test:


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;
    }
}

123voto

Gishu Points 59012

Un HierarchicalDataTemplate est une manière de dire "c'est une façon de rendre ce type d'objet et voici une propriété qui peut être sondé pour trouver les nœuds enfants en vertu de cet objet"

Par conséquent, vous avez besoin d'une propriété unique qui retourne les "enfants" de ce nœud. par exemple (Si vous ne pouvez pas faire à la fois du Groupe et de l'Entrée de dériver à partir d'un même type de Nœud)

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;
            }
        }

Ensuite, vous n'avez pas besoin d'un HierDataTemplate pour l'entrée depuis l'entrée n'ont pas d'enfants. Ainsi, le XAML doit être modifié pour utiliser les nouveaux Éléments de propriété et un DataTemplate d'Entrée:

<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>

Et voici ce que cela ressemble. Screenshot of Output

12voto

kiwipom Points 5336

Je pense que vous êtes la plupart du chemin... avec un petit peu de retravailler vous devriez obtenir ce travail assez facilement...

Je vous suggère de créer une base de classe abstraite (ou d'une interface, selon ce que vous préférez) et hériter de la/le mettre en œuvre pour à la fois le Groupe et la classe d'Entrée...

De cette façon, vous pouvez exposer une propriété au sein de votre Groupe d'objet

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

^à ce stade, vous pouvez prendre une décision si cela remplace vos listes de sous-groupes et les Entrées, ou simplement les ajoute ensemble et les renvoie dans le getter de la propriété...

Maintenant, tous vous avez besoin est de remplir la collection d'Enfants avec l'un ou l'autre Groupe ou à l'Entrée des objets, et l' HierarchicalDataTemplate rendra correctement lorsque les objets sont placés dans l'Arborescence..

Une dernière pensée, si l'Entrée est toujours le "bas niveau" de l'arbre (c'est à dire n'a pas d'enfants), alors vous n'avez pas besoin de définir un HierarchicalDataTemplate pour l'Entrée de l'objet, une DataTemplate suffit.

Espérons que cela aide :)

11voto

Matthew Points 6516

Voici une autre implémentation de la réponse de Gishu qui renvoie un IEnumerable plutôt qu'un IList et utilise le mot clé yield pour simplifier le code:

 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;
        }
    }
}
 

3voto

Anders Points 41

Cet article m'a aidé à chercher une solution au même problème: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

en utilisant MultiBinding et CompositeCollectionConverter ..

/ Cordialement Anders

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X