【问题标题】:How to show nested relationship in TreeView如何在 TreeView 中显示嵌套关系
【发布时间】:2014-11-19 05:21:15
【问题描述】:

努力在我的TreeView 中显示嵌套关系。这是场景:

在数据库中,我有 Category 和 Account 表。每个类别可以有零个或多个子类别,因此该表与其自身具有嵌套关系。每个类别/子类别可以有零个或多个帐户,Category 和Account 之间存在一对多关系。很简单,不是吗!

在我的数据库之上,我有 EDMX,其中包含 Categories 和 Accounts 实体及其关联,如上所述。为了便于理解,我重命名了导航属性,以便 Categories 现在包含 ParentCategory、ChildCategories 和 Accounts 属性。

在 EDMX 之上,我有我的 ViewModel,它定义了一个名为 AllCategories 的公共属性。我的 TreeView 将绑定到此属性。我在启动时这样初始化这个属性:

using (MyEntities context = new MyEntities())
    Categories = context.Categories.Include(x => x.Accounts).ToList();

最后我用下面的HierarchicalDataTemplate 来展示这个东西:

 <HierarchicalDataTemplate DataType = "{x:Type local:Category}" ItemsSource = "{Binding Path=ChildCategories}">
  <TreeViewItem Header="{Binding Name}" ItemsSource="{Binding Accounts}" />
</HierarchicalDataTemplate>

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

这运行良好,并在树中显示类别、子类别和帐户,但问题是子类别不仅显示在其父类别下,而且还显示在根级别。这发生在所有深度的类别中。我在这里做错了什么?

注意:如果我在 VM 中添加 .Where(x=&gt;!x.ParentID.HasValue),它只会显示根类别及其直接子类别,不会显示其他内容。

编辑

这是它目前的样子。一切都很好,直到虚线白线(我手动添加了那条线以进行说明;与 WPF 无关)。之后,子类别开始重复其子子类别。这个过程一遍又一遍地继续,直到叶子子类别。我相信我了解这里发生了什么,但没有解决方案。作为参考,this guy 提出了问题的解决方案,但他正在使用 DataSet,而我正在使用 EF,无法将他的解决方案转换为我的场景。

【问题讨论】:

  • 添加另一个包含类别而非子类别的分层模板。这将是您的树视图的根。
  • @deafjeff:谢谢。你能告诉它是什么样子吗?
  • 不明白你的问题,截图会很有帮助。此外,您以错误的方式使用HierarchicalDataTemplate,其中的根视觉控件应该是标题,因此通常我们需要在那个地方使用一些ContentControl,而不是您当前正在使用的 ItemsControl,例如 TreeViewItem。
  • 每个Category 都有一个子类别的集合和还有一个帐户的集合,那么您究竟想如何显示 2 个仅在 TreeViewItem 下的集合?看来您必须先合并它们。
  • @KingKing:我添加了当前状态的截图。外加一个指向解决此问题的代码项目文章的链接。

标签: wpf treeview hierarchicaldatatemplate


【解决方案1】:

这个想法是通过 ObservableCollections 连接您的业务数据,并让您的分层模板保持简单,这样树视图就不会显示重复的条目。 示例代码显示了嵌套的视图模型关系和相应的分层模板。为简化起见,Root 是一个 ObservableCollection(否则您需要在此处添加 INotifyPropertyChanged 并在 TreeView 中添加选择性 ItemsSource 绑定)

<Window x:Class="MyWpf.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyWpf"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <HierarchicalDataTemplate DataType = "{x:Type local:RootItem}" ItemsSource = "{Binding Path=Categories}">
        <TextBlock Text="{Binding Header}"></TextBlock>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType = "{x:Type local:CatNode}" ItemsSource = "{Binding Path=Items}">
        <TextBlock Text="{Binding Header}"></TextBlock>
    </HierarchicalDataTemplate>
</Window.Resources>
<Grid>
    <TreeView ItemsSource="{Binding MyRoot}"/>
</Grid>
</Window>

namespace MyWpf
{
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        MyRoot = new ObservableCollection<RootItem>();
        MyRoot.Add(new RootItem());
    }
    public ObservableCollection<RootItem> MyRoot { get; set; }
}

public class RootItem
{
    public RootItem()
    {
        Categories = new ObservableCollection<CatNode>();
        Categories.Add(new CatNode { Header = "Cat1" });
        Categories[0].Items.Add("Item11");
        Categories[0].Items.Add("Item12");
        Categories.Add(new CatNode { Header = "Cat2" });
        Categories[1].Items.Add("Item21");
        Categories[1].Items.Add("Item22");
    }
    public string Header { get { return "Root"; }}
    public ObservableCollection<CatNode> Categories { get; set; }
}
public class CatNode
{
    public CatNode()
    {
        Items = new ObservableCollection<string>();
    }
    public string Header { get; set; }
    public ObservableCollection<string> Items { get; set; }
}
}

【讨论】:

  • 确实非常优雅。我后来找到了Josh Smith's excellent article on WPF TreeView binding,我推荐给任何想要使用 WPF TreeView 和 MVVM 的人。尽管那篇文章非常广泛,但基本思想与您阐述的相同。非常感谢。
  • 顺便说一句,我也是从那篇文章开始的! :-)
猜你喜欢
  • 2022-12-19
  • 1970-01-01
  • 1970-01-01
  • 2018-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
相关资源
最近更新 更多