我有一个如下所示的列表:
Base/Level1/Item1 Base/Level1/Item2 Base/Level1/Sub1/Item1 Base/Level2/Item1 Base/Level3/Sub1/Item1
我想要一个简单的方法将它放入ListView。 (即类似于此)
Base | +->Level1 | | | +=Item1 | +=Item2 | | | +->Sub1 | | | +=Item1 | +->Level2 | | | +=Item1 | +->Level3 | +->Sub1 | +=Item1
有没有一种既定的方法来进行这种转换,还是只需要自己解析一下?
(如果它可能是相关的,我的代码中的实际项目是TFS迭代路径。)
答案 0 :(得分:5)
这将获取您的字符串列表并将其转换为适合使用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属性。这可以直接绑定到TreeView
,方法是指定HierarchicalDataTemplate
ItemsSource="{Binding Children}"
,内容包含<TextBlock Text="{Binding Name}">
或类似内容。
或者,如果您需要其他成员或语义,可以在代码中定义树节点类。例如,给定此节点类:
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
直接绑定。我通常使用第一个解决方案(匿名类型),除非我想对树节点做一些特别的事情。
答案 1 :(得分:2)
WPF TreeView
可以使用HierarchicalDataTemplates
显示分层数据。但是,目前您的数据是平的,因此您必须将其转换为分层数据结构。没有内置的方法可以帮助你......
例如,您可以创建一个类:
class Node
{
public string Name { get; set; }
public List<Node> Children { get; set; }
}
将您的单位数据解析为包含子节点的Node
个对象列表,并在资源中创建一个TreeView
,其中包含以下HierarchicalDataTemplate
:
<TreeView ItemsSource="{Binding ListOfNodes}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
将根据您的数据自动生成树节点。如果您需要为层次结构的不同级别使用不同的类,请为每个类创建不同的HierarchicalDataTemplate
答案 2 :(得分:1)
更通用的实现可能就是这个。想象一下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