【转】四行代码创建复杂(无限级)树

http://www.cnblogs.com/ldp615/archive/2011/12/09/simple-create-a-complex-tree.html

最近两三天一直在做树方面的基础工作,碰巧今天在博客园看到一篇文章《C#中一种通用的树的生成方式》,粗略浏览下,感觉有不够强大。

对比而言,感觉自己的方式更好些,只需要四行代码就可以创建一颗复杂的无限级树。

在此分享一下,请大家先看两个运行截图:

image

image

下面,我们来看如何实现,先给出树节点的两个类:

树节点类

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
public class TreeNode {     private List<TreeNode> _children;      public TreeNode(string text, object value = null) {         this.Text = text;         this.Value = value;         _children = new List<TreeNode>();     }      public string Text { get; private set; }     public object Value { get; protected set; }     public TreeNode Parent { get; set; }     public IEnumerable<TreeNode> Children { get { return _children; } }      public void Add(TreeNode childNode) {         _children.Add(childNode);         childNode.Parent = this;     }     public void Remove(TreeNode childNode) {         _children.Remove(childNode);     }     public override string ToString() {         return Text;     } }

一个比较标准的树节点,有父结点和多个子节点。Value 属性用来保存和树结点相关的对象的值。

再加上一个泛型版本的:

1 2 3 4 5 6
public class TreeNode<T> : TreeNode {     public TreeNode(string text, T t)         : base(text, t) {     }     public new T Value { get { return (T)base.Value; } } }

算是一个强类型的树节点吧。

为了方便树节点的操作,我给 TreeNode 编写了一些扩展方法,如下给出本文要用的一个:

查找所有叶子节点的扩展方法

1 2 3 4 5 6 7 8 9 10 11 12
public static class TreeNodeExtensions {     public static IEnumerable<TreeNode> GetLeafNodes(this TreeNode treeNode) {         foreach (var child in treeNode.Children) {             if (child.Children.Any()) {                 foreach (var descendant in GetLeafNodes(child))                     yield return descendant;             }             else                 yield return child;         }     } }

一个迭推调用。

最后到了重点:

TreeBuilder 相关类

TreeBuilder 类:

1 2 3 4 5 6 7 8 9 10 11
public static class TreeBuilder {     public static BuildRootContext Build(string text) {         var root = new TreeNode(text);         return new BuildRootContext(root);     }     internal static TreeNode<T> BuildNode<T>(T t, Func<T, string> textSelect = null) {         var text = textSelect != null ? textSelect(t) : Convert.ToString(t);         var node = new TreeNode<T>(text, t);         return node;     } }

BuildRootContext 类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public class BuildRootContext {     private TreeNode _tree;      public BuildRootContext(TreeNode tree) {         this._tree = tree;     }      public BuildChildrenContext<T> SetItems<T>(IEnumerable<T> items) {         foreach (var item in items) {             var node = TreeBuilder.BuildNode(item);             _tree.Add(node);         }         return new BuildChildrenContext<T>(_tree);     }     public TreeNode Tree { get { return _tree; } } }

BuildChildrenContext 类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
public class BuildChildrenContext<T> {     private TreeNode _tree;      public BuildChildrenContext(TreeNode tree) {         this._tree = tree;     }      public TreeNode Tree { get { return _tree; } }      public BuildChildrenContext<V> SetItems<V>(Func<T, IEnumerable<V>> itemSelector, Func<V, string> textSelect = null) {         var leafNodes = _tree.GetLeafNodes().OfType<TreeNode<T>>();         foreach (var leafNode in leafNodes) {             foreach (var child in itemSelector(leafNode.Value)) {                 var node = TreeBuilder.BuildNode(child, textSelect);                 leafNode.Add(node);             }         }         return new BuildChildrenContext<V>(_tree);     }     public BuildChildrenContext<T> SetRecursiveItems(Func<T, IEnumerable<T>> itemSelector,         Func<T, string> textSelect = null) {         var context = this;         while (_tree.GetLeafNodes().OfType<TreeNode<T>>().Any(n => itemSelector(n.Value).Any()))             context = context.SetItems<T>(itemSelector, textSelect);         return context;     } }

通过这三个类即可达到文首图片中的效果,但实现可不是最佳方式,效率没太考虑,命名也不太好。

不过可以运行,先用上,慢慢改进吧!

简单使用说明

有限级树

像第一张图片中的产品树,只有两级:类别和产品,使用如下代码创建:

1 2 3 4
var tree = TreeBuilder.Build("产品")     .SetItems(categories)     .SetItems(category => products.Where(p => p.Category == category))     .Tree;

第 1 行,构建根结点,传入一个字符串作为根结点的文本。

第 2 行,指定树的第一级,必须传入一个集合,上面传入的是 IEnumerable<Category>。

第 3 行,指定树的第二级,传入的是 Func<Category, IEnumerable<Product>>。

第 4 行,调用 Tree 属性,返回根结点,也是树了。

如果需要更多的级数,可将代码写在调用 Tree属性之前,也就是第 3 行和第 4 行之间。

增加一级,可以写成下面的样子(可没什么实际意义):

1 2 3 4 5
var tree = TreeBuilder.Build("产品")     .SetItems(categories)     .SetItems(category => products.Where(p => p.Category == category))     .SetItems(product=>product.Name)     .Tree;

无限级树

第二张图片中的员工树是无限级的,因为员工的下属还可能有下属,是没有限制级数的。使用 SetRecursiveItems 方法构建:

1 2 3 4
var tree = TreeBuilder.Build("员工树")     .SetItems(employees.Where(e => e.ReportsTo == null))     .SetRecursiveItems(e => e.Subordinates)     .Tree;

其代码它部分和有限级树相同。

还可以在员工树增加层次,之上增加部门,之后增加爱好等等,调用 SetItmes 即可。

部门无限然后员工无限,也是可以的。

总结

通过 TreeBuilder 可以让我们极其方便的创建很复杂的树,让我们把更多的精力放在业务逻辑上(而不是程序或界面逻辑)。

文中代码编写仓促,如有 Bug 请在回复中告知。

如果本文对你有帮助或启发,不妨推荐一下让更多的朋友看到。

 

在线演示:

你可以通过下面两个网址,看到通过本文的代码生成的树:

源码下载:TreeDemo.rar (15KB) 

posted @ 2011-12-21 15:58  swjm119  阅读(205)  评论(0编辑  收藏  举报