数据展示
数据源绑定需要设置KeyFieldName和ParentFieldName后绑定数据
treeList1.DataSource = dataTable1;
且KeyFieldName列不能存在重复的数据
手动增加节点
var node = treeList1.AppendNode("", null);
注:该方法有多个重载,可灵活设置节点的父节点,图标,勾选状态,tag等
设置node上的数据
node.SetValue(treeListColumn1, obj1);
node.Tag = obj2;
外观
自定义设置节点的图标(需要绑定一个imageCollection给TreeList的SelectImageList)private void treeListMaster_CustomDrawNodeImages(object sender, DevExpress.XtraTreeList.CustomDrawNodeImagesEventArgs e)
{
var index = e.Node.GetValue("IMAGEINDEX");
if (index.ToString() != string.Empty)
{
e.SelectImageIndex = Convert.ToInt32(index);
}
}
其他外观设置基本都在OptionsView下面,可以设置:
是否显示行头,列头,水平线,垂直线,勾选框等。
OptionsFind下可以设置搜索栏的显示方式,是否显示搜索,清空按钮等。但是这个自带的搜索比较鸡肋,当一个节点满足条件,但是其父节点不满足条件时这个节点也无法显示。
除非事先调用treeList1.ExpandAll();将节点全部展开显示,当数据比较大的时候也是比较麻烦的,后面会介绍一种自定义的方法。
拖曳操作
如果需要从外面拖入数据,需要设置treeList1.AllowDrop = true;
如果需要内部拖曳节点,需要设置
treeList1.OptionsBehavior.DragNodes = true;
相关的几个事件:
DragEnter——当拖曳进入到treeList中时
DragOver——相当于在treeList上的MouseMove
DragDrop——拖曳结束松开鼠标时
DragLeave——拖曳节点出treeList时
从外部控件(比如GridControl)拖曳数据到树上,流程:
//1
Grid的MouseMove事件判断是否左键按下,执行treeList1.DoDragDrop(yourData, DragDropEffects.Copy);
//2
treeList的DragEnter事件,执行e.Effect = DragDropEffects.Copy;//可以执行判断,如果不满足可赋值DragDropEffects.None禁止拖曳操作
//3
treeList的DragDrop事件,获取数据e.Data.GetData(typeof(YourType)) as YourType,并判断当前节点:
var hi = treeListMaster.CalcHitInfo(treeListMaster.PointToClient(new Point(e.X, e.Y)));
var targetNode = hi.Node;
此处可通过hi.HitInfoType判断当前拖放的位置,是Cell,Column,Row,StateImage,Button,FilterPanel,ScrollBar,CheckBox....
然后执行treeList1.AppendNode(...);即可。
将树节点拖出控件执行删除操作,想当然地在DragLeave中操作,问题来了:
1.操作后,treeList依然认为在执行Drag操作,除非找到一个时机将DragDropEffects置为None;
2.体验不好,鼠标刚刚离开树,还没抬起左键,提示的MessageBox就弹出来了,不是想要的效果;
多次实验后还是换为另一种思路(这其实是DevExpress的Demo):
在treeList外部放置一个label,图标设置为垃圾桶:
private void labelControl1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
labelControl1.Appearance.ImageIndex = 1;//换为打开的垃圾桶
}
private void labelControl1_DragDrop(object sender, DragEventArgs e)
{
//这里执行删除节点操作
DeleteNode();
labelControl1.Appearance.ImageIndex = 0;//换为关闭的垃圾桶
}
private void labelControl1_DragLeave(object sender, EventArgs e)
{
labelControl1.Appearance.ImageIndex = 0;//换位关闭的垃圾桶
}
树内部拖曳,移动或节点排序也可以在DragDrop中操作
treeList1.SetNodeIndex(dragnode, treeList1.GetNodeIndex(targetnode));
然后保存这个顺序即可。
问题来了:如何判断是移动节点还是排序?在拖曳的时候树控件虽然会提示箭头(向上/下表示移动,向右表示加为子节点),但是在DragDrop的代码中却无法判断这个状态。
无奈只好改为其他的事件来处理移动节点和节点排序:
AfterDragNode——拖曳节点结束后触发
通过比对dragNode的拖曳前后的父节点,判断是排序还是移动节点。
其他的还有DragObjectDrop,这个名字比较坑,其实只在拖曳treeListColumn时触发。
节点搜索过滤
前面提到treeList提供了OptionsFind,可以展示一个Find框,可以将任何匹配的文字高亮,如果打开了FilterNode选项还可以实现节点过滤。但是存在一个问题:如果某节点匹配条件,但其父节点不匹配,该节点仍被过滤掉。除非在Find之前将树全部展开treeList1.ExpandAll();明显我们不希望这样。
根据DevExpress的官方答复,他们会在后续版本中提供一个新的OptionsFilter.FilterMode:Extended,但是目前的版本还没有。
实现方法,放置一个ButtonEdit在树控件的上方,并实现一个FilterNodeOperation类:
class FilterNodeOperation : TreeListOperation
{
string pattern;
public FilterNodeOperation(string _pattern)
{
pattern = _pattern;
}
public override void Execute(TreeListNode node)
{
if (NodeContainsPattern(node, pattern))
{
node.Visible = true;
//if (node.ParentNode != null)
// node.ParentNode.Visible = true;
//必须要递归查找其父节点全部设置为可见
var pNode = node.ParentNode;
while (pNode != null)
{
pNode.Visible = true;
pNode = pNode.ParentNode;
}
}
else
node.Visible = false;
}
bool NodeContainsPattern(TreeListNode node, string pattern)
{
foreach (TreeListColumn col in node.TreeList.VisibleColumns)
{
if (node.GetDisplayText(col).Contains(pattern))
return true;
}
return false;
}
}
//在ButtonEdit的ButtonClick事件中执行这个Operation:
private void buttonEdit1_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e)
{
var operation = new FilterNodeOperation(buttonEdit1.EditValue != null ? buttonEdit1.EditValue.ToString() : "");
treeListMaster.NodesIterator.DoOperation(operation);
}