Is it possible to define a TreeView hierarchical view template for a 'plain' collection?

偶尔善良 提交于 2019-12-12 03:16:53

问题


I have my model classes that look like this:

class Base 
{ 
    public string Name { get; set; }
}

class Item : Base { ... }

class Group : Base 
{
    public List<Base> GroupItems { get; private set; }
}

Then, I have a view model with an Items collection, that I fill up like shown in the FillUp() method below:

class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Base> Items { get; set; }

    private void FillUp()
    {
        Item item1 = new Item { Name = "Item1" };
        Item item2 = new Item { Name = "Item2" };
        Group group = new Group { Name = "Group" };
        group.GroupItems.Add(item1);
        this.Items.Add(item1);
        this.Items.Add(item2);
        this.Items.Add(group);
    }
}

So, my Items collection now contains 2 objects of type Item and one object of type Group, which has a "parent-child" reference to one of the Item object.

What I want to achieve: I'd like to display this model in a TreeView so that all the Items which belong to some Group should be displayed as child elements of that Group, despite of they are in the "root" collection.

It should then look like this:

- Item2
+ Group
   - Item1

I could use a HierarchicalDataTemplate to display parent-child elements, but this does not let me filtering out those items from the root collection that belong to some groups.

<TreeView>
  <TreeView.Resources>
    <HierarchicalDataTemplate DataType = "{x:Type loc:Group}" ItemsSource = "{Binding GroupItems}">
      <TextBlock Text="{Binding Name}"/>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type loc:Item}">
      <TextBlock Text="{Binding Name}"/>
    </DataTemplate>
  </TreeView.Resources>
  <TreeViewItem ItemsSource="{Binding Items}"/>
</TreeView>

With this view, I'm getting the "Item1" twice:

- Item1
- Item2
+ Group
    - Item1

Is there any way to get the desired result via view (XAML) markup only? Otherwise, I should change my view model so that the Items will be only added into the Groups, not in the "root" collection (but that is what I try to avoid at the moment).


回答1:


Assuming you have some indication of which Item belongs in a group , for this example iv'e added a Guid in Group and Item classes.

You can go about this in 2 ways :

1) Create a composite wrapper class . And create an ItemsSource of Wrapper class . Where there is a single item which does not belong to a group , Children would just remain null.

public class Wrapper<T> where T : Base
{
    public List<T> Children { get; set; }
}

public class Base 
{ 
    public string Name { get; set; }
}

public class Item : Base
{
    public Guid GroupId { get; set; }
}

public class Group : Base 
{
    public Guid ID { get; set; }
    public List<Item> GroupItems { get; private set; }
}

your second option is to use a CollectionView , and filter out Items which belong to a group.

private ICollectionView _baseView;
public ICollectionView BaseView
{

    get
    {
        if (_baseView == null)
        {
            _baseView = new ListCollectionView((IList) items);
            _baseView.Filter = o => o is Group || o is Item && (o as Item).GroupId != Guid.Empty;                   
        }
        return _baseView;
    }
}



回答2:


You can define two Collections in your ViewModel one for all items and one for the tree, it seems to be simple.

class ViewModel
{
    public ObservableCollection<Base> AllItems { get; set; }
    public ObservableCollection<Base> RootLevelItems { get; set; }
}

If you have only two levels (e.g. no nested groups) you can use ListView with GroupStyle.




回答3:


In response to your comment:

There are no duplicates in the collection by the way, as you can see in my code - it's just the view that displays the item twice.

You said that there are no duplicates in your code, but clearly there is:

private void FillUp()
{
    Item item1 = new Item { Name = "Item1" };
    Item item2 = new Item { Name = "Item2" };
    Group group = new Group { Name = "Group" };
    group.GroupItems.Add(item1);  // Adding item1 into collection in Group 
    this.Items.Add(item1);  // Adding item1 into collection again
    this.Items.Add(item2);
    this.Items.Add(group);
}

Therefore, as I said, this is the expected behaviour and the TreeView is only showing you what you told it to. So to remove the duplicate item, simply don't add it to your collection:

private void FillUp()
{
    Item item1 = new Item { Name = "Item1" };
    Item item2 = new Item { Name = "Item2" };
    Group group = new Group { Name = "Group" };
    group.GroupItems.Add(item1);  // Adding item1 into collection in Group 
    // this.Items.Add(item1);  // Don't add item1 into collection again
    this.Items.Add(item2);
    this.Items.Add(group);
}


来源:https://stackoverflow.com/questions/28340249/is-it-possible-to-define-a-treeview-hierarchical-view-template-for-a-plain-col

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!