首页 > 解决方案 > 如何绑定分层列表到 WPF 树视图

问题描述

我有一个分层类型Category,我想放入TreeView。嵌套级别计数是无限的。数据存储在带有hierarchyid字段的数据库中。

类定义

public class Category
{
    public Category()
    {
        NestedCategories = new List<Category>();
    }

    public string CategoryParentID { get; set; }
    public string CategoryHID { get; set; }
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public string CategoryValueType { get; set; }
    public DateTime LastTimeUsed { get; set; }

    public List<Category> NestedCategories
    {
        get; set;
    }

    public void AddChild(Category cat)
    {
        NestedCategories.Add(cat);
    }

    public void RemoveChild(Category cat)
    {
        NestedCategories.Remove(cat);
    }

    public List<Category> GetAllChild()
    {
        return NestedCategories;
    }
}

首先,我从表中取出所有数据并将其放入结构化列表中。我在调试器中检查了结果,它实际上包含了所有级别的类别。

public CategorySelector()
{
    InitializeComponent();
    catctrl = new CategoryController();

    Categories = CategoriesExpanded();

    DataContext = this;
}

private readonly CategoryController catctrl;

public List<Category> Categories { get; set; }

private List<Category> CategoriesExpanded()
{
    List <Category> list = catctrl.GetAllCategories();
    foreach (Category cvo in GetAllCat(list))
    {
        foreach (Category newparent in GetAllCat(list))
        {
            if (newparent.CategoryHID.ToString().Equals(cvo.CategoryParentID.ToString()))
            {
                list.Remove(cvo);
                newparent.AddChild(cvo);
                break;
            }
        }
    }
    return list;
}

private List<Category> GetAllCat(List<Category> list)
{
    List<Category> result = new List<Category>();
    foreach (Category child in list)
    {
        result.AddRange(GetNestedCat(child));
    }
    return result;
}

private List<Category> GetNestedCat(Category cat)
{
    List<Category> result = new List<Category>();
    result.Add(cat);
    foreach (Category child in cat.NestedCategories)
    {
        result.AddRange(GetNestedCat(child));
    }
    return result;
}

然后我在 XAML 中添加绑定,就出现了问题。我尝试了不同的组合,但我得到的只是第一级显示。

<mah:MetroWindow x:Class="appname.Views.CategorySelector"
        xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:localmodels="clr-namespace:appname.Models"
                 mc:Ignorable="d"
        .....
        <TreeView Name="TV_Categories" Grid.Row="5" FontSize="16" ItemsSource="{Binding Categories}" DisplayMemberPath="CategoryName">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type localmodels:Category}" ItemsSource="{Binding NestedCategories}" >
                    <TextBlock Text="{Binding CategoryName}" />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</mah:MetroWindow>

那么我做错了什么?谢谢你。

标签: c#wpfxamldata-binding

解决方案


我认为您使用了错误的 XAML 语言版本。有多个XAML 语言版本。WPF 仅完全支持2006版本。仅部分支持2009版本。

在 WPF 中,您可以使用 XAML 2009 功能,但仅适用于不是 WPF 标记编译的 XAML。标记编译的 XAML 和 XAML 的 BAML 形式目前不支持 XAML 2009 语言关键字和功能。

这些是正确的2006语言 XML 命名空间:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

在您的DataType定义中,您xmlns在属性元素上使用,这是2009 年语言功能

XAML 2009 可以支持属性元素上的 XAML 命名空间 (xmlns) 定义;但是,XAML 2006 仅支持对象元素上的 xmlns 定义。

如果您的项目不符合上述约束,则不能在 WPF 中使用它。相反,您可以在对象元素上声明本地 XML 命名空间,例如您的顶级元素(此处Window)并使用x:Type标记扩展或简单地说DataType="localmodels:Category",例如:

<Window x:Class="YourApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:localmodels="clr-namespace:YourApp"
<HierarchicalDataTemplate DataType="{x:Type localmodels:Category}" ItemsSource="{Binding NestedCategories}" >
   <TextBlock Text="{Binding CategoryName}" />
</HierarchicalDataTemplate>

更新,我找到了根本原因。如果设置DisplayMemberPaththis 将导致TreeView应用仅显示ToString()相应属性值的文本的数据模板。它不知道您的嵌套类别集合。

现在,如果您将数据模板直接分配给TreeView.ItemTemplate除了设置之外DisplayMemberPath,您将收到一个异常,指出您不能同时使用两者。

System.InvalidOperationException:'不能同时设置“DisplayMemberPath”和“ItemTemplate”。'

但是,如果您在 中定义数据模板,Resources也不例外,它会静默失败并应用DisplayMemberPath模板,这就是为什么只显示一个级别的原因。

为了解决问题,只需删除DisplayMemberPath所有这些变体即可。

<TreeView Name="TV_Categories" Grid.Row="5" FontSize="16" ItemsSource="{Binding Categories}">
   <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding NestedCategories}" >
         <TextBlock Text="{Binding CategoryName}" />
      </HierarchicalDataTemplate>
   </TreeView.Resources>
</TreeView>
<TreeView Name="TV_Categories" Grid.Row="5" FontSize="16" ItemsSource="{Binding Categories}">
   <TreeView.ItemTemplate>
      <HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding NestedCategories}" >
         <TextBlock Text="{Binding CategoryName}" />
      </HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>
<TreeView Name="TV_Categories" Grid.Row="5" FontSize="16" ItemsSource="{Binding Categories}">
 <TreeView.ItemTemplate>
      <HierarchicalDataTemplate ItemsSource="{Binding NestedCategories}" >
         <TextBlock Text="{Binding CategoryName}" />
      </HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>

推荐阅读