在树视图中显示类似于文件系统的层次结构,涉及泛型

时间:2016-09-27 07:41:28

标签: c# wpf treeview hierarchicaldatatemplate

我有一些代表用户库的类。它们看起来像这样:

public class UserLibraryData
{
    public IList<Node<UserDataEntry>> Nodes { get; private set; }
    ...
}

public class Node<T>
{
    public IList<Node<T>> SubNodes { get; private set; }
    public IList<T> Entries { get; private set; }
    public string Name { get; private set; }
    ...
}

public class UserDataEntry
{
    string ViewName { get; private set;}
    ...
}

因此它就像一个包含文件夹(节点)和文件(条目)的文件系统。

现在我想在TreeView中表示该数据。我写了一个XAML文件,其实例为UserLibraryData作为其DataContext。对于TreeView,我写了这个:

    <TreeView ItemsSource="{Binding Nodes}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding SubNodes}">
                <TextBlock Text="{Binding Name}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

但正如预期的那样,它只显示节点和子节点,而不是条目。我尝试将另一个DataTemplate添加到TreeViewTemplate,但似乎我只能定义一个这样的模板。

那么我如何在每一层中显示节点的条目呢?

编辑:

假设我们有以下UserLibraryData:

Node1 -> contains Node2, Node3 and Entry1, Entry2
Node2 -> contains Entry3, Entry4
Node3 -> contains Node4
Node4 -> contains Entry5
Node5 -> contains nothing

这是我的预期输出(扩展所有节点时):

Node1
    Node2
        Entry3
        Entry4
    Node3
        Node4
            Entry5
    Entry1
    Entry2
Node5

EDIT2:

如果Node类不是通用的,那么这个答案https://stackoverflow.com/a/39721198/5333340就可以解决我的问题。不幸的是,正如我从其他SO线程中学到的那样,DataType="{x:Type ...}"不允许泛型类型。一个常见的解决方法是创建一个像这样的子类

public class NodeUserDataEntry : Node<UserDataEntry> { ... }

然后将Node<UserDataEntry>的每次出现替换为NodeUserDataEntry。但是,已经编写了大量代码,这些代码大量使用了Node的通用性。因此,将类型从Node<UserDataEntry>更改为NodeUserDataEntry就像重新编写整个应用程序一样。是不是还有另一种方法可以保留我已编写的UserLibraryDataNode<T>UserDataEntry代码,只是扩展它,可能是通过某些转换器/包装器?

2 个答案:

答案 0 :(得分:1)

我采用了你的数据结构并添加了一些缺失的东西,以便它能够正常工作。

您的问题如下...... 在构建HierarchicalDataTemplate时,您无法选择两个列表ItemsSource。因此,您无法告诉您的模板显示SubNodes中的EntriesNode

为了能够构建TreeView,您必须构建一个包含ListEntries对象的唯一SubNodes

我称之为Childrens。 (在节点类中添加它。)

  public List<object> Childrens
    {
        get 
        {
            _childrens = new List<object>();
            foreach (var subNode in SubNodes)
            {
                _childrens.Add(subNode);
            }
            foreach (var userDataEntry in Entries)
            {
                _childrens.Add(userDataEntry);
            }
            return _childrens;
        }
        private set { _childrens = value; }
    }

在此之后你必须改变你的XAML。

我将模板声明为ressources,并且由于DataTypes,模板将在运行时解析。

我们希望展示:

    来自班级Name
  • Node
  • 来自班级ViewName
  • Entry
  • 来自班级Subnodes的{​​{1}}媒体资源中包含
  • EntriesChildrens。 (这揭示了以前的两个属性)。

    Node

假设您的DataContext设置为Class UserLibraryData,您必须以这种方式声明TreeView:

<Grid.Resources>

    <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Childrens}">
        <TextBlock Text="{Binding Name}"/>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type local:UserDataEntry}">
        <TextBlock Text="{Binding ViewName}"/>
    </DataTemplate>

</Grid.Resources>

你应该得到这个结果:

enter image description here

答案 1 :(得分:0)

我找到了另一种解决方法:

  • 按照https://stackoverflow.com/a/39721198/5333340的建议将Childrens属性添加到课程Node<T>

  • 将班级ViewName的媒体资源UserDataEntry重命名为Name

  • ItemsSource的{​​{1}}更改为HierarchicalDataTemplate

由于我的XAML代码中的"{Binding Childrens}"没有设置HierarchicalDataTemplate属性,因此它用于DataType集合的所有元素(Childrens和{{ 1}}),因为它们都具有属性Node<T>,所以它们都显示出来。

这比在代码中的各处更改类型要容易得多。

但我仍然感兴趣的是一个解决方案,我不需要更改已有的代码,只需要扩展它。