DXP TreeList 目录树
1.需求背景
需要一个支持勾选
,拖动节点
,保存各节点顺序
的目录树。
2.创建目录树
在treeList
控件中添加两个colunm
用来显示绑定数据
和显示
值。
接下来对treeList
的属性进行设置
// 设置列不显示 treeList.OptionsView.ShowColumns = false; // 设置序号列不显示 treeList.OptionsView.ShowIndicator = false; // 设置垂直线不显示 treeList.OptionsView.ShowVertLines = false; // 设置水平线不显示 treeList.OptionsView.ShowHorzLines = false; // 设置焦点框为行焦点 treeList.OptionsView.FocusRectStyle = DevExpress.XtraTreeList.DrawFocusRectStyle.RowFocus; // 隐藏第一列(数据列) treeListColumn1.Visible = false; // 设置不可编辑 treeList.OptionsBehavior.Editable = false; // 设置显示复选框 treeList.OptionsView.ShowCheckBoxes = true; // 设置勾选父节点,子节点自动全选 treeList.OptionsBehavior.AllowRecursiveNodeChecking = true;
添加节点
private TreeListNode AppendNode(PrjTableNode node, int pid) { TreeListNode treeListNode = null; Actionint> ac = (arg1, arg2) => { treeListNode = treeList.AppendNode(new object[] { node, node.Alias }, pid, 0, 0, 0); }; var tt = treeList.Invoke(ac, new object[] { node, pid }); SetNodeCheckState(treeListNode); return treeListNode; }
主要用到treeList.AppendNode()
方法,方法定义如下
AppendNode(object nodeData, int parentNodeId, int imageIndex, int selectImageIndex, int stateImageIndex)
- nodeData : object 类型的参数,这里传入
object[]
数组对象,数组对应treeList
的列,这里第一列是数据,第二列用来显示,因此需要将第一列隐藏。 - parentNodeId :父节点ID
- imageIndex : 节点图标索引,这里没有图标就给任意一个数字
- selectImageIndex : 节点被选择后显示的图标索引
- stateImageIndex : 状态图标索引
此时目录树就创建好了。
2.1 设置目录树选中节点的背景色
添加CustomDrawNodeCell
事件
// 设置行背景色 treeList.CustomDrawNodeCell -= TreeList_CustomDrawNodeCell; treeList.CustomDrawNodeCell += TreeList_CustomDrawNodeCell;
设置颜色
private void TreeList_CustomDrawNodeCell(object sender, DevExpress.XtraTreeList.CustomDrawNodeCellEventArgs e) { if (e.Node.Selected) { e.Appearance.BackColor = Color.FromArgb(192, 192, 255); } }
2.2 控制目录树节点的勾选框是否显示
添加CustomDrawNodeCheckBox
事件
// 控制复选框显隐 treeList.CustomDrawNodeCheckBox -= TreeList_CustomDrawNodeCheckBox; treeList.CustomDrawNodeCheckBox += TreeList_CustomDrawNodeCheckBox;
private void TreeList_CustomDrawNodeCheckBox(object sender, DevExpress.XtraTreeList.CustomDrawNodeCheckBoxEventArgs e) { // 满足逻辑条件的 ,将 e.Handled = true 即可 if ((e.Node.GetValue(treeListColumn1) as PrjTableNode)?.Type == ConstClass1.PRJ_TYPE_ID) { //e.Handled = true; } }
2.3 节点拖拽
这里的需求是只允许同级节点内部拖动,也不允许拖动到节点子集。
设置属性OptionsDragAndDrop.DragNodesMode = DragNodesMode.Single
添加DragOver
,DragDrop
,AfterDragNode
事件
// 设置节点拖拽 treeList.OptionsDragAndDrop.DragNodesMode = DragNodesMode.Single; // 处理拖动时的逻辑 treeList.DragOver -= TreeList_DragOver; treeList.DragOver += TreeList_DragOver; // 处理拖动结束时的逻辑 treeList.DragDrop -= TreeList_DragDrop; treeList.DragDrop += TreeList_DragDrop; // 处理拖动后的逻辑 treeList.AfterDragNode -= TreeList_AfterDragNode; treeList.AfterDragNode += TreeList_AfterDragNode;
DragOver 用来处理拖动时的逻辑
当有节点需要禁止拖动时,满足逻辑时,设置 e.Effect = DragDropEffects.None;
即可
private void TreeList_DragOver(object sender, DragEventArgs e) { var currNode = treeList.FocusedNode; var curNodeData = currNode.GetValue(treeListColumn1) as PrjTableNode; if (curNodeData == null) { return; } if (!curNodeData.CanDrag) { e.Effect = DragDropEffects.None; } }
DragDrop 用来处理拖动结束时的逻辑
private void TreeList_DragDrop(object sender, DragEventArgs e) { // 当前节点的父节点变化,则不允许拖动 var dragNode = e.Data.GetData(typeof(TreeListNode)) as TreeListNode; var sourceParent = dragNode.ParentNode.GetValue(treeListColumn1) as PrjTableNode; var targetNode = treeList.CalcHitInfo(treeList.PointToClient(MousePosition)).Node; if (targetNode == null) { return; } PrjTableNode targetNodeParent = null; if (targetNode.ParentNode != null) { targetNodeParent = targetNode.ParentNode.GetValue(treeListColumn1) as PrjTableNode; } // 发生跨级移动 if (sourceParent.Id != targetNodeParent.Id) { e.Effect = DragDropEffects.None; return; } // 移到子集 if (AjustDirection(sender, e) == DragInsertPosition.AsChild) { e.Effect = DragDropEffects.None; return; } }
/// /// 移动过程中的方向 /// /// /// /// private DragInsertPosition AjustDirection(object sender, DragEventArgs e) { var tl = sender as TreeList; //var dragNode = e.Data.GetData(typeof(TreeListNode)) as TreeListNode; //var hit = tl.CalcHitInfo(tl.PointToClient(new Point(e.X, e.Y))); var pi = typeof(TreeList).GetProperty("Handler", BindingFlags.Instance | BindingFlags.NonPublic); var handler = (TreeListHandler)pi.GetValue(tl, null); return handler.StateData.DragInfo.DragInsertPosition; }
AfterDragNode 用来处理拖动结束后的逻辑
private void TreeList_AfterDragNode(object sender, AfterDragNodeEventArgs e) { // TODO:... }
3.总结
treeList
是一个很强大的控件,用来处理树状结构。本次需求中,处理节点拖拽话费了较长时间,也网上找了很多博文,但是都没有直接解决我的问题,因此在这里做个笔记。
后记:纸上得来终觉浅,绝知此事要躬行