すべてのグループがネストされたグループを表示し、各グループがエントリを表示するように、コレクションをバインドする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);
}
}
}
<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>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
LoadView();
}
private void LoadView()
{
TestData data = new TestData();
data.Load();
GroupView.ItemsSource = data.Groups;
}
}
HierarchicalDataTemplate
は「これがこのタイプのオブジェクトをレンダリングする方法であり、このオブジェクトの下の子ノードを見つけるためにプローブできるプロパティです」と言う方法です。
したがって、このノードの「子」を返す単一のプロパティが必要です。例えば(グループとエントリの両方を共通の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;
}
}
次に、エントリには子がないため、エントリにHierarchicalDataTemplate
は必要ありません。そのため、XAMLを変更して、新しいItemsプロパティとエントリにDataTemplate
を使用する必要があります。
<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>
そして、それは次のようになります。
私はあなたがそこのほとんどの道にいると思います...ほんの少しの手直しで、あなたはこれをかなり簡単に機能させるべきです...
基本抽象クラス(またはインターフェイス、どちらでも構いません)を作成し、GroupクラスとEntryクラスの両方に継承/実装することをお勧めします...
そうすれば、Groupオブジェクト内でプロパティを公開できます
public ObservableCollection<ITreeViewItem> Children { get; set; }
^この時点で、サブグループとエントリのリストを置き換えるか、単にそれらを一緒に追加してプロパティゲッターで返すかを決定できます...
必要なのは、ChildrenコレクションにGroupオブジェクトまたはEntryオブジェクトのいずれかを入力することだけです。オブジェクトがTreeViewに配置されると、HierarchicalDataTemplate
が正しくレンダリングされます。
最後に、Entryが常にツリーの「最下位レベル」(つまり、子を持たない)である場合、EntryオブジェクトにHierarchicalDataTemplate
を定義する必要はありません。DataTemplate
で十分です。 。
お役に立てれば :)
以下は、Gishuの回答の代替実装であり、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;
}
}
}
この投稿は、同じ問題の解決策を探すときに役立ちました: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html
multiBindingとCompositeCollectionConverterを使用します。
/よろしくAnders