写在前面
C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。
树状结构
树状结构能够凸显数据的层次性,其基本单位为结点(Node),和继承关系类似,所有结点只能有唯一的父结点(Parent),而可以有多个子结点(Children),父子关系是树状结构的基本关系。根据下图(3叉树)需要明确树状结构的几个基本概念:
概念 | 含义 |
---|---|
根结点(Root) | 无父结点的结点 |
叶结点(Leaves) | 无子结点的结点 |
N叉树 | N为所有结点子结点数最大值 |
层次(Level) | 根结点层次为1,其他结点层次为其父结点层次+1,如A和B的层次都为2 |
兄弟结点(Brothers) | 父结点相同的结点,如A和B |
目录树
VS实现的TreeView(目录树)控件是一种特殊的树状结构,其本身的目的不是为了查找而是为了展示数据,可以当做是多个线性结构(Nodes)的组合,常用的二叉查找树AVL树、红黑树等的树状查找方法不能直接用在目录树上。
注意:TreeView对象本身代表树的根结点,所以展示的实际根结点也会有父结点,即TreeView对象。
点击树结点
这种情况通常发生在需要打开被点击结点对应的文件时。
- 如果被点击的结点为叶结点,点击打开对应文件;
- 如果被点击的结点非叶结点,实际上其对应的是文件夹而非文件,不做出响应。
首先为TreeView添加NodeMouseClick或NodeMouseDoubleClick事件,事件中的TreeNodeMouseClickEventArgs参数可以直接获取被点击的结点,TreeView的SelectedNode属性也可以获取因为点击被选中的结点,更加推荐后者,在点击后应该判断SelectedNode是否为null再执行其他操作。
树结点搜索
以下代码可以实现两种搜索功能,但只限于最大层次为2的目录树:
- 当搜索关键词包含于父结点时,搜索结果会展示该父结点下的所有子结点;
- 当搜索关键词多次出现在不同父结点的子结点时,搜索结果会展示所有满足条件的子结点及其所属父结点。
将搜索框命名为txtSearch,TreeView对象命名为directoryTree,首先为搜索按钮添加Click事件,在Click事件中添加代码
if (txtSearch.Text == "")
{
MessageBox.Show("请在搜索框内输入文件名称");
return;
}
BuildDirectoryTree();
List<TreeNode> nodeList = new List<TreeNode>();
SearchNode(directoryTree.Nodes, txtSearch.Text.ToUpper(), nodeList);
if (nodeList.Count == 0)
{
MessageBox.Show("未搜索到指定文件!");
}
else
{
TreeNode[] parents = new TreeNode[directoryTree.Nodes.Count];
List<TreeNode>[] childrenGroups = new List<TreeNode>[directoryTree.Nodes.Count];
foreach (TreeNode node in nodeList)
{
if (node.Parent == null)
parents[node.Index] = node;
else
{
if (childrenGroups[node.Parent.Index] == null)
childrenGroups[node.Parent.Index] = new List<TreeNode>();
childrenGroups[node.Parent.Index].Add(node);
}
}
directoryTree.Nodes.Clear();
for (int i = 0; i < parents.Length; i++)
{
if (parents[i] != null)
directoryTree.Nodes.Add(parents[i]);
else if (childrenGroups[i] != null)
{
TreeNode parent = childrenGroups[i][0].Parent;
parent.Nodes.Clear();
foreach (TreeNode child in childrenGroups[i])
parent.Nodes.Add(child);
directoryTree.Nodes.Add(parent);
}
}
directoryTree.ExpandAll();
directoryTree.Refresh();
}
其中,BuildDirectoryTree方法是重新构建目录树,也就是在每次搜索前都需要将目录树还原到初始状态,SearchNode方法代码如下:
/// <summary>
/// 搜索文件结点,即叶节点
/// </summary>
/// <param name="nodes">搜索树结点</param>
/// <param name="name">搜索名称大写形式</param>
/// <param name="nodeList">保存搜索结果的结点列表</param>
private void SearchNode(TreeNodeCollection nodes, string name, List<TreeNode> nodeList)
{
if (nodes.Count != 0)
{
foreach (TreeNode node in nodes)
{
if (node.Text.ToUpper().Contains(name))
nodeList.Add(node);
else
SearchNode(node.Nodes, name, nodeList);
}
}
}