前段时间写项目文档,发现Word导航栏标题的节点搜索还是蛮有意思的,想着自己也实现一下这样的逻辑。下图就是Word节点搜索时的图。
仔细观察搜索时的行为,可以分析需要实现以下功能:
1、节点递归查找
2、颜色标注匹配节点
3、若匹配节点其父节点尚未展开,则需颜色标注该父节点,表示其子项存在匹配项
4、展开节点时,若节点并非是匹配节点,则取消颜色标注
5、收回节点时,若节点含有匹配子节点,则需按颜色标注
项目中常常用到DevExpress,TreeList中有个CustomDrawNodeCell事件,可以很好控制每个节点显示的颜色。下面就用Winform+Dev控件简单实现这个功能。
界面没什么好说的,就拖几个控件就好。
TreeList数据源,大家可以根据项目需要绑定,我这里是自己随便生成的。
class Data
{
public static DataTable InitData()
{
List lists = GenerateData();
ModelHandler modelHandler = new ModelHandler();
return modelHandler.FillDataTable(lists);
}
public static List GenerateData()
{
List lists = new List() {
new NodeModel(){ID=101 ,PARENTID=100,NAME="第一类"},
new NodeModel(){ID=102 ,PARENTID=100,NAME="第二类"},
new NodeModel(){ID=103 ,PARENTID=100,NAME="第三类"},
new NodeModel(){ID=104 ,PARENTID=100,NAME="第四类"},
new NodeModel(){ID=105 ,PARENTID=100,NAME="第五类"},
new NodeModel(){ID=106 ,PARENTID=100,NAME="第六类"},
new NodeModel(){ID=107 ,PARENTID=101,NAME="张三"},
new NodeModel(){ID=108 ,PARENTID=113,NAME="张三"},
new NodeModel(){ID=109 ,PARENTID=103,NAME="李四"},
new NodeModel(){ID=110 ,PARENTID=104,NAME="王五"},
new NodeModel(){ID=111 ,PARENTID=105,NAME="黄四"},
new NodeModel(){ID=112 ,PARENTID=106,NAME="王六"},
new NodeModel(){ID=113 ,PARENTID=102,NAME="第二子类"}
};
return lists;
}
}
class NodeModel
{
public int ID { get; set; }
public int PARENTID { get; set; }
public string NAME { get; set; }
}
为了实现上述的功能,逻辑应该是这样的,就是先对节点向下查找,对查找到的匹配节点再进行向上查找父节点,将匹配节点与其父节点区别标记,去重后形成一个集合,将这个集合交给绘制事件,绘制时根据属性判断是否需要颜色标注。
因此需要写一个节点扩展类,至少包含三个属性,如是否是匹配的节点,是否需要颜色标注,节点的id。
class NodeEx
{
private bool _matchNode;
public bool MatchNode { get { return _matchNode; } }
public bool DisPlay { set; get; }
private int _id;
public int ID { get { return _id; } }
private string _Name;
public string NAME { get { return _Name; } }
public NodeEx(bool matchNode,TreeListNode node)
{
_matchNode = matchNode;
_id = (int)node.GetValue("ID");
_Name = node.GetValue("NAME").ToString();
}
}
class Compare:IEqualityComparer
{
public bool Equals(NodeEx x,NodeEx y)
{
return x.ID == y.ID;
}
public int GetHashCode(NodeEx obj)
{
return obj.ID.GetHashCode();
}
}
继续实现递归查找方法,以及节点展开事件、节点收缩事件、节点绘制事件
public partial class Form1 : Form
{
private List listResult = new List();//查询结果list
private List listDisplay = new List();//绘制事件list
private string filterText = "";//搜索文本
public Form1()
{
InitializeComponent();
InitStyle();
}
private void InitStyle()
{
treeList1.Tag = "DATA";
treeList1.KeyFieldName = "ID";
treeList1.ParentFieldName = "PARENTID";
treeList1.DataSource = Data.InitData();
}
private void simpleButton1_Click(object sender, EventArgs e)
{
listResult.Clear();
listDisplay.Clear();
filterText = textEdit1.Text;
if (string.IsNullOrWhiteSpace(filterText))
{
treeList1.Refresh();
return;
}
SearchNodes(treeList1.Nodes);
listDisplay.AddRange(listResult);
listDisplay = listDisplay.Distinct(new Compare()).ToList();
treeList1.Refresh();
}
private void SearchNodes(TreeListNodes nodes)
{
foreach (TreeListNode node in nodes)
{
HighLightNodes(node);
// 如果当前节点下还包括子节点,就调用递归
if (node.Nodes.Count > 0)
{
SearchNodes(node.Nodes);
}
}
}
private void HighLightNodes(TreeListNode node)
{
string nodeName = node.GetValue("NAME").ToString();
if (nodeName.Contains(filterText.ToUpper()) || nodeName.Contains(filterText.ToLower()))
{
NodeEx nodeEx = new NodeEx(true, node);
nodeEx.DisPlay = true;
listResult.Add(nodeEx);
SearchParentNode(node);
}
}
///
/// 寻找父节点 是否应该以颜色标注
///
///
private void SearchParentNode(TreeListNode node)
{
if (node.ParentNode != null)
{
var rs = listResult.Where(x => x.ID == (int)node.ParentNode.GetValue("ID")).FirstOrDefault();
//如果没有查找到,则加入list 确认是否需要颜色标注
//如果查找到,则不需做处理
if (rs == null)
{
NodeEx nodeEx = new NodeEx(false, node.ParentNode);
if (node.ParentNode.Expanded)
{
nodeEx.DisPlay = false;
}
else
{
nodeEx.DisPlay = true;
}
listDisplay.Add(nodeEx);
SearchParentNode(node.ParentNode);
}
}
}
private void treeList1_AfterExpand(object sender, NodeEventArgs e)
{
var rs = listDisplay.Where(x => x.ID == (int)e.Node.GetValue("ID")).FirstOrDefault();
if (rs != null)
{
if (!rs.MatchNode)
{
rs.DisPlay = false;
}
treeList1.RefreshNode(e.Node);
}
}
private void treeList1_AfterCollapse(object sender, NodeEventArgs e)
{
var rs = listDisplay.Where(x => x.ID == (int)e.Node.GetValue("ID")).FirstOrDefault();
if (rs != null)
{
if (!rs.MatchNode)
{
rs.DisPlay = true;
}
treeList1.RefreshNode(e.Node);
}
}
private void treeList1_CustomDrawNodeCell(object sender, DevExpress.XtraTreeList.CustomDrawNodeCellEventArgs e)
{
var lists = listDisplay.Where(x => x.DisPlay == true).ToList();
foreach (var nodeEx in lists)
{
if ((int)e.Node.GetValue("ID")==nodeEx.ID)
{
e.Appearance.BackColor = Color.Yellow;
}
}
}
}
最后上实现的效果