programing

WPF TreeView HierarchicalDataTemplate-여러 자식 컬렉션이있는 개체에 바인딩

nicescript 2021. 1. 18. 07:38
반응형

WPF TreeView HierarchicalDataTemplate-여러 자식 컬렉션이있는 개체에 바인딩


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

A HierarchicalDataTemplate는 '이 유형의 개체를 렌더링하는 방법이며 여기에이 개체 아래의 자식 노드를 찾기 위해 조사 할 수있는 속성이 있습니다'라고 말하는 방법입니다.

따라서이 노드의 '자식'을 반환하는 단일 속성이 필요합니다. 예 (공통 노드 유형에서 그룹과 항목을 모두 파생시킬 수없는 경우)

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

Next you don't need a HierarchicalDataTemplate for entry since an entry doesn't have children. So the XAML needs to be changed to use the new Items property and a DataTemplate for 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>

And here's what that looks like. Screenshot of Output


I think you are most of the way there... with a tiny bit of rework you should get this working fairly easily...

I would suggest you create a base abstract class (or an interface, whichever you prefer) and inherit/implement it for both the Group and Entry class...

That way, you can expose a property within your Group object

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

^at this point, you can make a decision if this replaces your lists of SubGroups and Entries, or merely appends them together and returns them in the property getter...

Now all you need is to populate the Children collection with either Group or Entry objects, and the HierarchicalDataTemplate will render correctly when the objects are placed in the TreeView..

One final thought, if Entry is always the 'bottom level' of the tree (ie has no children) then you do not need to define a HierarchicalDataTemplate for the Entry object, a DataTemplate will suffice.

Hope this helps :)


Here is an alternative implementation of Gishu's answer that returns an IEnumerable rather than an IList, and makes use of the yield keyword to simplify the 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;
        }
    }
}

This post helped me out when looking for a solution for tha same issue: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

using MultiBinding and CompositeCollectionConverter..

/Regards Anders

ReferenceURL : https://stackoverflow.com/questions/1912481/wpf-treeview-hierarchicaldatatemplate-binding-to-object-with-multiple-child-co

반응형