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

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

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

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

树节点类

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;}elseyield 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 请在回复中告知。

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

在线演示:

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

  • 产品树:http://demo.highsoft.cc/products/tree
  • 员工树:http://demo.highsoft.cc/employees/multi-select-tree

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

四行代码创建复杂(无限级)树相关推荐

  1. php创建无限级树型菜单以及三级联动菜单

    http://www.php.cn/php-weizijiaocheng-373500.html 这篇文章主要介绍了php创建无限级树型菜单 ,主要使用的是递归函数,感兴趣的小伙伴们可以参考一下 写递 ...

  2. php动态创建菜单,php创建无限级树型菜单

    写递归函数,可考虑缓存,定义一些静态变量来存上一次运行的结果,多程序运行效率很有帮助.. 大概步骤如下: step1:到数据库取数据,放到一个数组, step2:把数据转化为一个树型状的数组, ste ...

  3. android 如何实现无限列表,在Android中解析和创建无限/无限级别的List /子列表中的XML...

    在我的Android Application的服务器端应用程序也由我开发.在这个应用程序Android应用程序从服务器请求一些XML并解析它. XML包含描述应用程序中应该有多少标签的信息,并且每个标 ...

  4. 词云可视化——四行代码轻松上手

    所需的Python第三方模块: wordcloud.imageio.jieba与matplotlib 安装命令如:pip install wordcloud 1号词云:(四行代码上手) # 导入词云制 ...

  5. Python四行代码实现的猜数字小游戏,基于thinker,带GUI界面

    Python四行代码实现的猜数字小游戏,基于thinker,带GUI界面 from tkinter import * from tkinter import messagebox 导入提示框 from ...

  6. AndroidStudio git 提交代码,创建分支,合并分支,回滚版本,拉取代码

    主要有: 提交代码,创建分支,合并分支,回滚版本,拉去代码 1 首先电脑中下载git 2 新建的项目把.git 仓库放到项目总中as 工具的右下角 会显示 Git:master 点击有一个弹框如下 然 ...

  7. akka actor java_Akka:使用非默认构造函数在Scala中定义一个actor并从Java代码创建它 - java...

    Akka Scala演员必须扩展akka.actor.Actor Akka Java actor必须扩展akka.actor.UntypedActor 因此,在使用非默认构造函数定义Scala act ...

  8. silverlight中递归构造无限级树treeview+checkbox

    两个实体,其实一个实体也能构造出来,我这里是为了增加一个 checkbox //第一个实体 public class person { public int no { get; set; } publ ...

  9. 在Qt中使用C++代码创建界面

    好儿郎~志在四方 Qt视频教程地址:http://space.bilibili.com/84360636/#!/index 目录视图 摘要视图 订阅 图灵赠书--程序员11月书单    [思考]Pyt ...

最新文章

  1. 人脸识别经典算法一:特征脸方法(Eigenface)
  2. MySQL 账户管理
  3. 你可能不知道的小知识-bug为什么叫bug
  4. vivo C/C++工程师视频面试总结 20180802
  5. 事务隔离级别和传播行为_Spring五个事务隔离级别和七个事务传播行为
  6. unity开发文档_Unity以赞助人身份加入Blender开发基金
  7. 活动目录实战之六 使用ADMT 3.2迁移用户和计算机
  8. 移远EC20 4G模块Linux驱动移植和测试
  9. 【Verilog TestBench教程】
  10. 【数据挖掘案例】财政收入影响因素分析及预测模型
  11. 《东周列国志》第七十二回 棠公尚捐躯奔父难 伍子胥微服过昭关
  12. fNIRS–EEG监测人脑活动和氧合作用的研究进展
  13. 日本药妆店扫货必备手册·收藏版
  14. 2020/2/23如何高效使用Axure绘制原型图
  15. css 实现马赛克背景,ps透明背景,未选颜色的展示方式
  16. Web3.0峰会上IPFS最新消息利好不断
  17. Python日常小技巧(持续更新中)
  18. Solr - DIH详解(上卷)
  19. 2009-2010年中国十大平面设计公司排名
  20. pstack无法查看进程堆栈“Could not attach to target”问题

热门文章

  1. 延迟加载的一些知识和误区
  2. Python正则表达式模式备忘表
  3. 存储过程,触发器,Mysql权限,备份还原
  4. 【No.4 Ionic】修改 cordova 插件
  5. ubuntu14.04 设置静态ip
  6. Discuz修改笔记-Discuz代码的使用
  7. 代码整洁之道 垃圾编码收集
  8. Linux 索引节点 inode
  9. 通道的分离与合并,ROI,
  10. 酒店客房管理系统任务汇报1