关于 DevExpress.XtraTreeList.TreeList 树形控件 的操作
作为一个C#程序员,在写程序时一直以来都使用的微软那一套控件,用起来特别爽,可是最近公司的一个项目用到了DevExpress框架,不用不知道,一用吓一跳,不得不承认这个框架确实很强大,效果也很炫,但是用这个框架也遇到了很多让我头疼的问题,由于该控件可能使用的人少,在网上能查到的解决办法实在有限,所以在解决问题时效率低得让我有些接受不了,所以再此把我遇到的一些问题和解决思路以及解决结果给大家分享一下,该篇博文只针对DevExpress.XtraTreeList.TreeList 树形控件来说以下简称(Dev树形),let's do it\(≧▽≦)/:
首先罗列出所需功能:
1、树形的绑定(给数据源对树形进行绑定)
2、树形节点的查找(根据用户输入的名字进行模糊查询节点数据:重点在于根据子节点反向递归父节点)
3、树形的右键选中节点(在节点上点击右键时就选中节点)
4、根据ID选中节点(该功能着实让我头疼了好久)
依次展示出这几个功能的效果图:
树形的绑定:
由于 Dev树形 在树形绑定时需要特定格式,那么我们先来展示出 Dev树形 所要使用到的实体类代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BW.PropertyModel.Class { public class TreeListModel { #region 字段属性 private int id; /// <summary> /// 节点编号 /// </summary> public int ID { get { return id; } set { id = value; } } private int parentID; /// <summary> /// 父节点编号 /// </summary> /// <value>The parent ID.</value> public int ParentID { get { return parentID; } set { parentID = value; } } private string menuName; /// <summary> /// 菜单名称 /// </summary> public string MenuName { get { return menuName; } set { menuName = value; } } private object tag; /// <summary> /// 用于装节点对象 /// </summary> public object Tag { get { return tag; } set { tag = value; } } #endregion } }
解释一下这四个字段的用途:ID 是独一无二的,在树形绑定时可以使用数据库中你要绑定的那个对象的ID;ParentID用于 Dev树形 识别节点所属关系(也就是该节点属哪个节点);MenuName 用于 Dev树形 节点的显示名; Tag用于装你所需要的任意对象
接下来再附图给大家展示给 Dev树形 绑定这几个字段,第二张图是接着第一张未展示完的地方,这8个地方我就不一一解释了,自己下来仔细看看,他们的含义一目了然的,这里需要注意的一点是:visible这个属性;接下来说一说数据源的事:数据源要根据数据库中的数据进行递归处理,附上递归处理和初始化树形的代码:
/// <summary> /// 加载设备树形 /// </summary> public void InitFacilityTree() { List<TreeListModel> list = new List<TreeListModel>(); int systemId = 0; if (this.cmbSystemClassify.EditValue != null) { int.TryParse(this.cmbSystemClassify.EditValue.ToString(), out systemId); } List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = {0} order by ID asc", systemId)); //查询数据库中某个系统的数据 TreeList(0, ref list, temp); //这里传的0表示 根节点 this.tvFacilityModel.KeyFieldName = "ID"; //这里绑定的ID的值必须是独一无二的 this.tvFacilityModel.ParentFieldName = "ParentID"; //表示使用parentID进行树形绑定 this.tvFacilityModel.DataSource = list; //绑定数据源 this.tvFacilityModel.ExpandAll(); //默认展开所有节点 } /// <summary> /// 递归调用——正向生成树形所需结构 /// </summary> /// <param name="parentId">根节点的父编码默认为0</param> /// <param name="listData">树形绑定的数据集合</param> /// <param name="modelList">某系统的结构数据</param> private void TreeList(int parentId, ref List<TreeListModel> listData, List<BW.DMSystem.Core.Entity.ModelManageInfo> modelList) { List<BW.DMSystem.Core.Entity.ModelManageInfo> tmp = modelList.Where(model => model.ParentCode == parentId).ToList(); //这句话很重要,对数据进行筛选全靠它 foreach (var item in tmp) { TreeListModel temp = new TreeListModel(); temp.ID = item.ID; temp.MenuName = item.Name; temp.ParentID = parentId; temp.Tag = item; listData.Add(temp); TreeList(item.ID, ref listData, modelList); } }
我这里使用的树形是根据系统分了类的,你如果不需要可以不管 根据注释应该能猜出个大概 树形的绑定就算告一段落了
树形节点的查找:
效果图是最开始的第二张图
代码:
/// <summary> /// 搜索事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSearch_Click(object sender, EventArgs e) { List<TreeListModel> list = new List<TreeListModel>(); List<int> listId = new List<int>(); //这个集合用于存储已经加入了的父节点ID,因为有些子节点可能同在一个父节点下若重复插入同ID的数据会报错 string inputStr = this.txtModelName.Text.Trim(); string systemId = string.Empty; if (this.cmbSystemClassify.EditValue != null) { systemId = this.cmbSystemClassify.EditValue.ToString(); } else { systemId = "0"; } if (string.IsNullOrEmpty(inputStr)) { //如果为空则查询所有 List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = '{0}' order by ID asc", systemId)); TreeList(0, ref list, temp); } else { //查询出所有数据 List<BW.DMSystem.Core.Entity.ModelManageInfo> allData = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = '{0}' order by ID asc", systemId)); //查询出满足条件的 List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and IsModel = 1 and SystemID = '{0}' and Name like '%{1}%' order by ID asc", systemId, inputStr)); for (int i = 0; i < temp.Count; i++) { TreeListModel currNode = new TreeListModel(); currNode.ID = temp[i].ID; currNode.MenuName = temp[i].Name; currNode.ParentID = temp[i].ParentCode; currNode.Tag = temp[i]; list.Add(currNode); listId.Add(temp[i].ID); TreeList_fanxiang(temp[i].ParentCode, ref list, ref listId, allData); } } this.tvFacilityModel.KeyFieldName = "ID"; this.tvFacilityModel.ParentFieldName = "ParentID"; this.tvFacilityModel.DataSource = list; this.tvFacilityModel.ExpandAll(); } }
/// <summary> /// 递归调用——反向查找节点的根节点 /// </summary> /// <param name="currId">根节点的父编码默认为0</param> /// <param name="listData">树形绑定的数据集合</param> /// <param name="modelList">某系统的结构数据</param> private void TreeList_fanxiang(int parentId, ref List<TreeListModel> listData, ref List<int> listId, List<BW.DMSystem.Core.Entity.ModelManageInfo> modelList) { List<BW.DMSystem.Core.Entity.ModelManageInfo> tmp = modelList.Where(model => model.ID == parentId).ToList(); //正向和反向的最终区别就在于这句话 foreach (var item in tmp) { TreeListModel temp = new TreeListModel(); temp.ID = item.ID; temp.MenuName = item.Name; temp.ParentID = item.ParentCode; temp.Tag = item; if (!listId.Contains(item.ID)) { listData.Add(temp); listId.Add(item.ID); } TreeList_fanxiang(item.ParentCode, ref listData, ref listId, modelList); } }
根据注释理解代码的含义,节点查找也告一段落了。
树形节点的右键选中:
在节点上鼠标右键点击时默认是不会选中该节点的,这个功能说简单不简单说复杂也不复杂,我就直接附上代码来
/// <summary> /// 鼠标右键事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tvFacilityModel_MouseUp(object sender, MouseEventArgs e) 需要注意的是 这个事件不要搞错了 { DevExpress.XtraTreeList.TreeList tree = sender as DevExpress.XtraTreeList.TreeList; if ((e.Button == MouseButtons.Right) && (ModifierKeys == Keys.None) && (tvFacilityModel.State == TreeListState.Regular)) //这里的几个条件只有第一个条件有点用,其他的可有可无 { Point p = new Point(Cursor.Position.X, Cursor.Position.Y); //获取到鼠标点击的坐标位置 TreeListHitInfo hitInfo = tree.CalcHitInfo(e.Location); if (hitInfo.HitInfoType == HitInfoType.Cell) { tree.SetFocusedNode(hitInfo.Node); //这句话就是关键,用于选中节点 TreeListNode selectNode = this.tvFacilityModel.FocusedNode; BW.DMSystem.Core.Entity.ModelManageInfo modelObj = selectNode.GetValue(3) as BW.DMSystem.Core.Entity.ModelManageInfo; if (modelObj == null) { return; } if (modelObj.IsModel) { this.ModelID = modelObj.ID.ToString(); this.ModelName = modelObj.Name; popupMenu1.ShowPopup(p); } } } }
再来看看微软的右键选中节点的代码:
private void treeView1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { treeView1.SelectedNode = treeView1.GetNodeAt(e.X, e.Y);
} }
诶,有种说不出的悲伤。。。OK该功能已Over
根据ID选中节点:
这里的思路起始很简单,只要找出树形中已经绑定的数据源中某个节点的ID的值和传递过来的值一样就选中就行了,但是就是这样一个简单的思路 Dev树形 却搞了很多花样着实让我蛋疼不已啊,原本想直接遍历树形的节点的,但是调试中发现 this.tvFacilityModel.Nodes.Count 这句话居然返回的只有第一级的节点,那怎么办呢?——又只有用递归查找了涩 诶,附上递归查找代码:
/// <summary> /// 递归查找子节点 /// </summary> /// <param name="node"></param> /// <param name="ID"></param> /// <returns></returns> private TreeListNode nodes(TreeListNode node, string ID) { TreeListNode returnNodex = null; if (node.HasChildren) { for (int i = 0; i < node.Nodes.Count; i++) { if (node.Nodes[i].GetValue("ID").ToString() == ID) { returnNodex = node.Nodes[i]; break; } else { returnNodex = nodes(node.Nodes[i], ID); } } } return returnNodex; }
最后再使用 this.tvFacilityModel.FocusedNode = selectNode; 将返回来的节点设置给 Dev树形 就OK了。
经过这次对树形控件的使用,没有多大的收获,收获最大的就是对递归这种方式有了更深的认识,也熟练了不少,Dev树形控件我的痛啊!~~~~(>_<)~~~~
好了,也许我的代码不够精简,目前的技术也就写的出这样的代码,希望广大朋友不要介意,有什么意见和需要帮助请进行评论或留言,多多沟通有助于发展。