四行代码创建复杂(无限级)树
最近两三天一直在做树方面的基础工作,碰巧今天在博客园看到一篇文章《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)
四行代码创建复杂(无限级)树相关推荐
- php创建无限级树型菜单以及三级联动菜单
http://www.php.cn/php-weizijiaocheng-373500.html 这篇文章主要介绍了php创建无限级树型菜单 ,主要使用的是递归函数,感兴趣的小伙伴们可以参考一下 写递 ...
- php动态创建菜单,php创建无限级树型菜单
写递归函数,可考虑缓存,定义一些静态变量来存上一次运行的结果,多程序运行效率很有帮助.. 大概步骤如下: step1:到数据库取数据,放到一个数组, step2:把数据转化为一个树型状的数组, ste ...
- android 如何实现无限列表,在Android中解析和创建无限/无限级别的List /子列表中的XML...
在我的Android Application的服务器端应用程序也由我开发.在这个应用程序Android应用程序从服务器请求一些XML并解析它. XML包含描述应用程序中应该有多少标签的信息,并且每个标 ...
- 词云可视化——四行代码轻松上手
所需的Python第三方模块: wordcloud.imageio.jieba与matplotlib 安装命令如:pip install wordcloud 1号词云:(四行代码上手) # 导入词云制 ...
- Python四行代码实现的猜数字小游戏,基于thinker,带GUI界面
Python四行代码实现的猜数字小游戏,基于thinker,带GUI界面 from tkinter import * from tkinter import messagebox 导入提示框 from ...
- AndroidStudio git 提交代码,创建分支,合并分支,回滚版本,拉取代码
主要有: 提交代码,创建分支,合并分支,回滚版本,拉去代码 1 首先电脑中下载git 2 新建的项目把.git 仓库放到项目总中as 工具的右下角 会显示 Git:master 点击有一个弹框如下 然 ...
- akka actor java_Akka:使用非默认构造函数在Scala中定义一个actor并从Java代码创建它 - java...
Akka Scala演员必须扩展akka.actor.Actor Akka Java actor必须扩展akka.actor.UntypedActor 因此,在使用非默认构造函数定义Scala act ...
- silverlight中递归构造无限级树treeview+checkbox
两个实体,其实一个实体也能构造出来,我这里是为了增加一个 checkbox //第一个实体 public class person { public int no { get; set; } publ ...
- 在Qt中使用C++代码创建界面
好儿郎~志在四方 Qt视频教程地址:http://space.bilibili.com/84360636/#!/index 目录视图 摘要视图 订阅 图灵赠书--程序员11月书单 [思考]Pyt ...
最新文章
- 人脸识别经典算法一:特征脸方法(Eigenface)
- MySQL 账户管理
- 你可能不知道的小知识-bug为什么叫bug
- vivo C/C++工程师视频面试总结 20180802
- 事务隔离级别和传播行为_Spring五个事务隔离级别和七个事务传播行为
- unity开发文档_Unity以赞助人身份加入Blender开发基金
- 活动目录实战之六 使用ADMT 3.2迁移用户和计算机
- 移远EC20 4G模块Linux驱动移植和测试
- 【Verilog TestBench教程】
- 【数据挖掘案例】财政收入影响因素分析及预测模型
- 《东周列国志》第七十二回 棠公尚捐躯奔父难 伍子胥微服过昭关
- fNIRS–EEG监测人脑活动和氧合作用的研究进展
- 日本药妆店扫货必备手册·收藏版
- 2020/2/23如何高效使用Axure绘制原型图
- css 实现马赛克背景,ps透明背景,未选颜色的展示方式
- Web3.0峰会上IPFS最新消息利好不断
- Python日常小技巧(持续更新中)
- Solr - DIH详解(上卷)
- 2009-2010年中国十大平面设计公司排名
- pstack无法查看进程堆栈“Could not attach to target”问题