c# - 如何绑定分层列表到 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>
那么我做错了什么?谢谢你。
解决方案
我认为您使用了错误的 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>
更新,我找到了根本原因。如果设置DisplayMemberPath
this 将导致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>