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 :)
Esta publicación me ayudó cuando buscaba una solución para el mismo problema: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html
utilizando MultiBinding y CompositeCollectionConverter ..
/ Recuerdos Anders
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.