在项目中,时常会遇到存在上下级关系的数据。在呈现方面,按照传统方法,不得不组装TreeNode之后添加到TreeView 中,已实现树形数据的显示。如果项目中需要多处使用树,毫无疑问这将存在巨大的代码冗余,会有无数针对不同实体组件TreeNode的代码出现,整体风格糟糕至极。
在近期的项目中,很多地方拥有这样的上下级关系,如下图所示,可以清楚的看出它们直接的关系
在数据的查询上,一般对于Oracle不太熟悉的人,可能就会采用 in或者exist关键字来进行筛选, 实际上Oracle有已经有递归查询的语法存在,对于Oracle不太熟悉的看官,可以百度一下。
这里列出了示例:
select t.project_id,t.project_name,t.p_project_id,t.p_project_name,t.category from sys_project_info s start with s.category in (10,11,21) connect by prior s.project_id=s.p_project_id and s.project_name = s.p_project_name;
到这里数据的提取部分就已经完成,剩下的就是树形数据的组建过程,也就是数据的呈现。归根结底还是递归,在这里只是对递归过程进行了封装,可以直接移植到WPF或者Silverlight项目中,代码如下:
1 using Microsoft.Practices.Prism.ViewModel; 2 using System; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.Collections.ObjectModel; 6 using System.Linq; 7 using System.Runtime.Serialization; 8 using System.Text; 9 10 namespace SysManager.Utility 11 { 12 public class TreeNode<T> : NotificationObject where T : BaseEntity 13 { 14 private T _parentNode; 15 private T _currentNode; 16 private bool _isChecked; 17 private bool _canChecked; 18 19 /// <summary> 20 /// 父节点 21 /// </summary> 22 public dynamic Key { get; set; } 23 24 /// <summary> 25 /// 子节点 26 /// </summary> 27 public ObservableCollection<TreeNode<T>> ChildrenNodes { get; set; } 28 29 /// <summary> 30 /// 是否允许被选中 31 /// </summary> 32 public bool CanChecked 33 { 34 get { return _canChecked; } 35 set 36 { 37 if (_canChecked == value) 38 return; 39 _canChecked = value; 40 RaisePropertyChanged("CanChecked"); 41 } 42 } 43 44 /// <summary> 45 /// 是否处于被选中状态 46 /// </summary> 47 public bool IsChecked 48 { 49 get { return _isChecked; } 50 set 51 { 52 if (_isChecked == value) 53 return; 54 _isChecked = value; 55 RaisePropertyChanged("IsChecked"); 56 } 57 } 58 /// <summary> 59 /// 当前选中节点 60 /// </summary> 61 public T CurrentNode 62 { 63 get { return _currentNode; } 64 set 65 { 66 if (_currentNode == value) 67 return; 68 _currentNode = value; 69 RaisePropertyChanged("CurrentNode"); 70 } 71 } 72 /// <summary> 73 /// 当前节点的父级节点 74 /// </summary> 75 public T ParentNode 76 { 77 get { return _parentNode; } 78 set 79 { 80 if (_parentNode == value) 81 return; 82 _parentNode = value; 83 RaisePropertyChanged("ParentNode"); 84 } 85 } 86 87 public TreeNode() 88 { 89 this.CanChecked = true; 90 } 91 92 static TreeNode<T> staticNode; 93 94 /// <summary> 95 /// 将数据组织成一个拥有上下级管理的树形结构数据集合 96 /// </summary> 97 /// <typeparam name="TKey">主键的数据类型</typeparam> 98 /// <param name="nodeSource">执行过GroupBy语句的数据源</param> 99 /// <param name="resultTreeNode">结果数据</param> 100 /// <param name="keyName">子节点标识字段名称</param> 101 /// <param name="parentKeyName">父节点标识字段名称</param> 102 /// <param name="orderByKeys"></param> 103 /// <returns>已生成的树形结构实体</returns> 104 public TreeNode<T> GenerateTreeNodes<TKey>(IEnumerable<IGrouping<TKey, T>> nodeSource, TreeNode<T> resultTreeNode, string keyName, string parentKeyName) 105 { 106 var orderedSource = nodeSource.OrderBy(groupList => groupList.Key); 107 foreach (IGrouping<TKey, T> item in orderedSource) 108 { 109 var listChildrenNode = new ObservableCollection<TreeNode<T>>(); 110 object propertyParentValue = null; 111 112 foreach (var child in item) 113 { 114 //get children id 115 object propertyValue = GetCurrentItemPropertyValue(keyName, child); 116 //get parent id 117 if (propertyParentValue == null) 118 propertyParentValue = GetCurrentItemPropertyValue(parentKeyName, child); 119 120 if (propertyValue != propertyParentValue) 121 { 122 T parentNodeInstance = null; 123 if (propertyParentValue != null) 124 { 125 var nodeParent = nodeSource.Where(x => x.Key.ToString() == propertyParentValue.ToString()).FirstOrDefault(); 126 if (nodeParent != null) 127 parentNodeInstance = nodeParent.FirstOrDefault(); 128 } 129 var node = new TreeNode<T>() { CurrentNode = child, Key = propertyValue, ChildrenNodes = new ObservableCollection<TreeNode<T>>(), ParentNode = parentNodeInstance }; 130 listChildrenNode.Add(node); 131 } 132 133 } 134 135 TreeNode<T> parentNode = null; 136 foreach (var root in listChildrenNode) 137 { 138 staticNode = null; 139 parentNode = FindParentNode(resultTreeNode, item.Key); 140 if (parentNode != null) 141 break; 142 } 143 if (parentNode != null) 144 { 145 parentNode.ChildrenNodes = listChildrenNode; 146 } 147 else 148 listChildrenNode.ToList().ForEach(childNode => resultTreeNode.ChildrenNodes.Add(childNode)); 149 150 } 151 return resultTreeNode; 152 } 153 154 /// <summary> 155 /// 将一个拥有上下级关系的Tree转换为列表 156 /// </summary> 157 public IEnumerable TreeNode2LinkedList { get; private set; } 158 /// <summary> 159 /// 将treenode转换为列表 160 /// </summary> 161 /// <typeparam name="T"></typeparam> 162 /// <param name="originalSource"></param> 163 public void TreeNodeToLinkedList(TreeNode<T> originalSource) 164 { 165 if (originalSource != null && originalSource.ChildrenNodes != null) 166 { 167 if (originalSource.CurrentNode != null) 168 { 169 if (TreeNode2LinkedList == null) 170 TreeNode2LinkedList = new List<T>(); 171 (TreeNode2LinkedList as List<T>).Add(originalSource.CurrentNode); 172 } 173 foreach (var item in originalSource.ChildrenNodes) 174 { 175 TreeNodeToLinkedList(item); 176 } 177 } 178 } 179 180 private static object GetCurrentItemPropertyValue(string keyName, T child) 181 { 182 var property = child.GetType().GetProperties().Where(prop => prop.Name == keyName).FirstOrDefault(); 183 object propertyValue = null; 184 if (property != null) 185 propertyValue = property.GetValue(child, null); 186 return propertyValue; 187 } 188 189 /// <summary> 190 /// 寻找当前节点的父级节点 191 /// </summary> 192 /// <param name="rootNode">当前节点</param> 193 /// <param name="key">节点的Key</param> 194 /// <returns>父级节点</returns> 195 private TreeNode<T> FindParentNode(TreeNode<T> rootNode, dynamic key) 196 { 197 198 if (rootNode.ChildrenNodes == null) 199 return rootNode; 200 foreach (var children in rootNode.ChildrenNodes) 201 { 202 if (children.Key == key) 203 { 204 staticNode = children; 205 break; 206 } 207 else 208 if (staticNode == null) 209 FindParentNode(children, key); 210 } 211 if (rootNode.Key != null && (int)rootNode.Key == key) 212 return rootNode; 213 214 return staticNode; 215 216 } 217 } 218 }
//调用方法 var treeNode = new TreeNode<SysProjectInfoEntity>(); treeNode.GenerateTreeNodes<decimal?>(result.OfType<SysProjectInfoEntity>().GroupBy(proj => proj.ParentProjectId), treeNode, "ProjectId", "ParentProjectId");