c# wpf xaml treeview hierarchicaldatatemplate

c# - WPF TreeView HierarchicalDataTemplate: enlace al objeto con varias colecciones secundarias



xaml (4)

Estoy intentando que TreeView vincule mi colección para que todos los grupos muestren grupos anidados y cada grupo muestre la entrada.

¿Cómo puedo usar HierarchicalDataTemplate para que TreeView procese tanto la colección SubGroups como las Entries?

Los grupos muestran subgrupos y entradas:

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


Objetos:

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

Datos de prueba:

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


Aquí hay una implementación alternativa de la respuesta de Gishu que devuelve un IEnumerable lugar de un IList , y hace uso de la palabra clave yield para simplificar el código:

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


Creo que estás todo el camino hasta allí ... con un poco de retrabajo deberías hacer que esto funcione con bastante facilidad ...

Le sugiero que cree una clase abstracta base (o una interfaz, según lo que prefiera) y la herede / implemente tanto para la clase de Grupo como para la de Entrada ...

De esta forma, puedes exponer una propiedad dentro de tu objeto Group

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

^ en este punto, puede tomar una decisión si esto reemplaza sus listas de Subgrupos e Inscripciones, o simplemente los agrega juntos y los devuelve en el captador de propiedades ...

Ahora todo lo que necesita es completar la colección Children con objetos Group o Entry, y HierarchicalDataTemplate se procesará correctamente cuando los objetos se coloquen en TreeView.

Un último pensamiento, si la entrada es siempre el ''nivel inferior'' del árbol (es decir, no tiene hijos), entonces no es necesario definir una HierarchicalDataTemplate para el objeto de entrada, una DataTemplate será suficiente.

Espero que esto ayude :)



Una HierarchicalDataTemplate es una forma de decir ''así es como se renderiza este tipo de objeto y aquí hay una propiedad que se puede sondear para encontrar los nodos secundarios bajo este objeto''

Por lo tanto, necesita una sola propiedad que devuelva los ''hijos'' de este nodo. ej. (Si no puede hacer que tanto el Grupo como la Entrada deriven de un tipo de Nodo común)

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

A continuación, no necesita una entrada HierDataTemplate ya que una entrada no tiene hijos. Por lo tanto, es necesario cambiar el XAML para usar la nueva propiedad Items y un DataTemplate para 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>

Y esto es lo que parece.