示例代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using Kingdee.BOS; using Kingdee.BOS.Util; using Kingdee.BOS.Core; using Kingdee.BOS.Core.DynamicForm; using Kingdee.BOS.Core.DynamicForm.PlugIn; using Kingdee.BOS.Core.DynamicForm.PlugIn.Args; using Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel; using Kingdee.BOS.Core.Metadata; namespace JDSample.FormPlugIn.DynamicForm { /// <summary> /// 分批分层加载树节点 /// </summary> /// <remarks> /// 案例背景: /// 界面上使用树控件,需显示大量的节点; /// 如果一次性加载到客户端,显示渲染会非常慢; /// 因此,需要分层、分批下载 /// /// 案例说明: /// 新增一个动态表单,挂上本插件,并在界面有一个树控件 F_JD_TreeView; /// 界面初始化时,仅加载第一层节点; /// 用户点击节点时,才加载其包含的子节点,而且每次最多加载10个; /// 单层超过10个节点,通过"点击加载更多..."节点,分批下载 /// </remarks> [Description("分批分层加载树节点")] public class S160107TreeEdit : AbstractDynamicFormPlugIn { /// <summary> /// 本地变量,存储需加载到前端的所有节点信息,以及其是否已经加载标志 /// </summary> private Dictionary<string, NodeInfo> _dctNodes = new Dictionary<string, NodeInfo>(); /// <summary> /// 本地变量,存储已经被加载过的父节点Id,避免重复搜索其子节点,浪费时间 /// </summary> private HashSet<string> _loadedNodeIds = new HashSet<string>(); /// <summary> /// 界面初始化结束,触发此事件,通知插件开始加载树节点:在此事件,加载第一层节点 /// </summary> /// <param name="e"></param> public override List<TreeNode> GetTreeViewData(TreeNodeArgs e) { if (e.Key.EqualsIgnoreCase("F_JD_TreeView") == false) { // 需加载是其他树控件的节点,略过 return new List<TreeNode>(); } // 加载全部节点信息到内存 this.LoadNodes(); // 记录已经加载过根节点 this._loadedNodeIds.Add("0"); // 展开树控件节点 TreeView tv = this.View.GetControl<TreeView>("F_JD_TreeView"); tv.SetExpanded(true); // 构造根目录下的第一层节点并返回 List<TreeNode> nodes = this.BuildTreeNodes("0"); return nodes; } /// <summary> /// 用户点击节点时触发此事件:加载更多子节点 /// </summary> /// <param name="e"></param> public override void TreeNodeClick(TreeNodeArgs e) { if (e.Key.EqualsIgnoreCase("F_JD_TreeView") == false) { // 点击的是其他树控件,略过 return; } // 判断是否已经加载过此节点的子节点,如果加载过,则不再加载 if (this._loadedNodeIds.Contains(e.NodeId)) { return; } this._loadedNodeIds.Add(e.NodeId); TreeView tv = this.View.GetControl<TreeView>("F_JD_TreeView"); string parentId = e.NodeId; if (parentId.StartsWith("more")) {// 当前点击的节点,是"点击加载更多..." string[] keys = parentId.Split('|'); parentId = keys[1]; // 第2部分为父节点部分 // "点击加载更多..."节点已经被点击过,不再需要了,移除之 tv.RemoveNode(e.NodeId); } // 开始加载更多的子节点 List<TreeNode> childNodes = this.BuildTreeNodes(parentId); if (childNodes.Count > 0) { tv.AddNodes(parentId, childNodes); } } /// <summary> /// 构建树控件所需要的节点对象 /// </summary> /// <param name="parentId">父节点Id,为0表示第一层节点</param> /// <returns></returns> private List<TreeNode> BuildTreeNodes(string parentId) { List<TreeNode> nodes = new List<TreeNode>(); // 遍历全部节点,找指定节点中,未加载的子节点的子节点 int count = 0; int index = 0; foreach (var item in _dctNodes) { NodeInfo nodeInfo = item.Value; if (nodeInfo.Loaded == false && nodeInfo.ParentId == parentId) { nodeInfo.Loaded = true; count++; TreeNode node = new TreeNode() { id = nodeInfo.Id, text = nodeInfo.Caption, parentid = nodeInfo.ParentId, }; nodes.Add(node); } if (count >= 10) {// 本次加载超过了10个 // 生成一个特殊的节点(加载更多...),并停止本批加载更多子节点 TreeNode node = new TreeNode() { // 需要基于如下需求,生成一个特殊的节点Id // 1. 需要与其他普通节点进行区分:以more为前缀 // 2. 需要能够提取出父节点Id:包含父节点Id // 3. 每次产生的特殊节点Id不能重复:需包含当前节点索引 id = string.Format("more|{0}|{1}", parentId, index), text = "点击加载更多...", parentid = parentId, }; nodes.Add(node); break; } index++; } return nodes; } /// <summary> /// 到数据库加载全部节点信息:本示例直接手工构建一批有层次的节点信息 /// </summary> private void LoadNodes() { // 节点名称,包含其子节点的数量,以提示用户,可以展开查看子节点 // 第一层节点 _dctNodes.Add("1", new NodeInfo() { Id = "1", ParentId = "0", Caption = "江西(100)" }); _dctNodes.Add("2", new NodeInfo() { Id = "2", ParentId = "0", Caption = "广东(3)" }); // 循环添加100个第一层节点 for (int i = 3; i <= 100; i++) { _dctNodes.Add(i.ToString(), new NodeInfo() { Id = i.ToString(), ParentId = "0", Caption = string.Format("省份{0}(0)", i) }); } // 第二层节点: _dctNodes.Add("1.1", new NodeInfo() { Id = "1.1", ParentId = "1", Caption = "南昌(0)" }); _dctNodes.Add("1.2", new NodeInfo() { Id = "1.2", ParentId = "1", Caption = "九江(0)" }); _dctNodes.Add("1.3", new NodeInfo() { Id = "1.3", ParentId = "1", Caption = "赣州(0)" }); // 循环添加100个第二层节点 for (int i = 4; i <= 100; i++) { _dctNodes.Add(string.Format("1.{0}", i), new NodeInfo() { Id = string.Format("1.{0}", i), ParentId = "1", Caption = string.Format("城市{0}(0)", i) }); } _dctNodes.Add("0201", new NodeInfo() { Id = "0201", ParentId = "02", Caption = "广州(0)" }); _dctNodes.Add("0202", new NodeInfo() { Id = "0202", ParentId = "02", Caption = "深圳(2)" }); _dctNodes.Add("0203", new NodeInfo() { Id = "0203", ParentId = "02", Caption = "惠州(0)" }); // 第三层节点: _dctNodes.Add("020201", new NodeInfo() { Id = "020201", ParentId = "0202", Caption = "南山区" }); _dctNodes.Add("020202", new NodeInfo() { Id = "020202", ParentId = "0202", Caption = "宝安区" }); } } /// <summary> /// 节点信息对象 /// </summary> class NodeInfo { /// <summary> /// 本节点Id /// </summary> public string Id { get; set; } /// <summary> /// 父节点Id /// </summary> public string ParentId { get; set; } /// <summary> /// 本节点标题 /// </summary> public string Caption { get; set; } /// <summary> /// 是否已经加载标志:默认为false,未加载到前端 /// </summary> public bool Loaded { get; set; } } }