WPF - 将列表带到树的好方法
Posted
技术标签:
【中文标题】WPF - 将列表带到树的好方法【英文标题】:WPF - Good Way to take a list to a Tree 【发布时间】:2011-01-07 12:51:06 【问题描述】:我有一个如下所示的列表:
基础/Level1/Item1 基础/Level1/Item2 基础/Level1/Sub1/Item1 基础/Level2/Item1 基础/Level3/Sub1/Item1我想要一种简单的方法将其放入 ListView。 (即与此类似)
根据 | +->1 级 | | | +=项目1 | +=项目2 | | | +->子1 | | | +=项目1 | +->2 级 | | | +=项目1 | +->Level3 | +->子1 | +=项目1是否有一种既定的方法来进行这种转换,还是我只需要滚动我自己的解析器?
(如果可能相关,我的代码中的实际项目是 TFS 迭代路径。)
【问题讨论】:
你是说你没有分层数据结构?只是一个平面列表? 好吧,我将它从一个超出我理解的 XML 结构中提取出来。所以是的,我有一个平面列表。 (把它想象成一个我想在 TreeView 中显示的文件路径列表) 【参考方案1】:这将获取您的字符串列表并将其转换为适合使用 TreeView 查看的树,如您所述:
public IList BuildTree(IEnumerable<string> strings)
return
from s in strings
let split = s.Split("/")
group s by s.Split("/")[0] into g // Group by first component (before /)
select new
Name = g.Key,
Children = BuildTree( // Recursively build children
from s in grp
where s.Length > g.Key.Length+1
select s.Substring(g.Key.Length+1)) // Select remaining components
;
这将返回一个匿名类型树,每个类型都包含一个 Name 属性和一个 Children 属性。这可以通过指定HierarchicalDataTemplate
和ItemsSource="Binding Children"
和由<TextBlock Text="Binding Name">
或类似内容组成的内容直接绑定到TreeView
。
如果您需要额外的成员或语义,您也可以在代码中定义树节点类。例如,给定这个节点类:
public class Node
public string Name get; set;
public List<Node> Children get; set;
您的 BuildTree 函数会略有不同:
public List<Node> BuildTree(IEnumerable<string> strings)
return (
from s in strings
let split = s.Split("/")
group s by s.Split("/")[0] into g // Group by first component (before /)
select new Node
Value = g.Key,
Children = BuildTree( // Recursively build children
from s in grp
where s.Length > g.Key.Length+1
select s.Substring(g.Key.Length+1)) // Select remaining components
).ToList();
同样可以使用HierarchicalDataTemplate
直接绑定。我通常使用第一种解决方案(匿名类型),除非我想对树节点做一些特殊的事情。
【讨论】:
【参考方案2】:WPF TreeView
可以使用HierarchicalDataTemplates
显示分层数据。但是,目前您的数据是扁平的,因此您必须将其转换为分层数据结构。没有内置的方法可以为您做到这一点...
例如,您可以创建一个这样的类:
class Node
public string Name get; set;
public List<Node> Children get; set;
将您的平面数据解析为带有子节点的Node
对象列表,并在资源中使用以下HierarchicalDataTemplate
创建一个TreeView
:
<TreeView ItemsSource="Binding ListOfNodes">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="x:Type local:Node" ItemsSource="Binding Children">
<TextBlock Text="Binding Name" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
树节点将根据您的数据自动生成。如果层次结构的不同级别需要不同的类,请为每个类创建不同的HierarchicalDataTemplate
【讨论】:
【参考方案3】:更通用的实现可能是这个。想象一个Node
类定义为:
public class Node<TItem, TKey>
public TKey Key get; set;
public int Level get; set;
public IEnumerable<TItem> Data get; set;
public List<Node<TItem, TKey>> Children get; set;
还有两个通用的IEnumerable<T>
扩展方法:
public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, params Func<TItem, TKey>[] keySelectors)
return list.ToTree(0, keySelectors);
public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, int nestingLevel, params Func<TItem, TKey>[] keySelectors)
Stack<Func<TItem, TKey>> stackSelectors = new Stack<Func<TItem, TKey>>(keySelectors.Reverse());
if (stackSelectors.Any())
return list
.GroupBy(stackSelectors.Pop())
.Select(x => new Node<TItem, TKey>()
Key = x.Key,
Level = nestingLevel,
Data = x.ToList(),
Children = x.ToList().ToTree(nestingLevel + 1, stackSelectors.ToArray())
)
.ToList();
else
return null;
您可以使用这些方法将用户对象的平面列表聚合到树中,具有任意聚合级别和更优雅的语法。唯一的限制是聚合键的类型必须相同。
例子:
class A
public int a get;set;
public int b get;set;
public int c get;set;
public int d get;set;
public string s get;set;
public A(int _a, int _b, int _c, int _d, string _s)
a = _a;
b = _b;
c = _c;
d = _d;
s = _s;
void Main()
A[] ls =
new A(0,2,1,10,"one"),
new A(0,1,1,11,"two"),
new A(0,0,2,12,"three"),
new A(0,2,2,13,"four"),
new A(0,0,3,14,"five"),
new A(1,0,3,15,"six"),
new A(1,1,4,16,"se7en"),
new A(1,0,4,17,"eight"),
new A(1,1,5,18,"nine"),
new A(1,2,5,19,"dunno")
;
var tree = ls.ToTree(x => x.a, x => x.b, x => x.c, x => x.d);
注意:此实现不是“真正的”树,因为没有单个根节点,但您可以很容易地实现 Tree<TItem, TKey>
包装类。
HTH
【讨论】:
以上是关于WPF - 将列表带到树的好方法的主要内容,如果未能解决你的问题,请参考以下文章