// 添加根节点public virtual int Add(TreeNode node)
// NodeText参数为字符串对象,在节点上显示为节点的text对象(Node.text)
this.treeView1.Nodes.Add(new TreeNode(NodeText));
// 添加其他节点,只能通过parent.Nodes.add()来添加
// 此处添加了一个一级节点
// this.treeView1.Nodes[i]表示treeview控件的第i个根节点对象
this.treeView1.Nodes[i].Nodes.Add(new TreeNode(NodeText));
//添加节点,其父节点不包含相同名称的子节点
//遍历其同级对象,看text中是否已经有了相同名称
foreach (TreeNode TNode in this.treeView1.SelectedNode.Nodes)
{
if (TNode.Text == NodeText)//NodeText为待添加的子节点的text
{
MessageBox.Show("已存在相同名称节点!");
return;
}
}
//不重名就添加子节点
this.treeView1.SelectedNode.Nodes.Add(new TreeNode(NodeText));
//指定text,该节点不能删除。
//this.treeView1.SelectedNode为选中节点对象。
if (this.treeView1.SelectedNode.Text == "不能删除节点")
{
MessageBox.Show("该文件不可删除!");
return;
}
//提示是否删除**节点,确认后使用待删除节点的Remove()函数删除
if (MessageBox.Show("是否删除【" + this.treeView1.SelectedNode.Text + "】节点?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK)
this.treeView1.SelectedNode.Remove();
//删除指定index节点
this.treeView1.Nodes[i].Remove();//删除第i个根节点
this.treeView1.Nodes[i].Nodes[j].Remove();//删除第i个根节点的第j个一级节点
当treeview节点对象过多时,我们为了查找节点更为方便,需要使用button来控制节点的展开与折叠
//展开节点
//treeview1所有节点全部展开
this.treeView1.ExpandAll();
//展开选中节点的下一级子节点
this.treeView1.SelectedNode.Expand();
//展开选中节点的所有子节点(包括以下多个层级所有子节点)
this.treeView1.SelectedNode.ExpandAll();
//折叠节点
this.treeView1.CollapseAll();//折叠treeview所有节点
this.treeView1.SelectedNode.CollapseAll();//折叠选中节点下的所有节点
节点重命名操作,首先需要进入treeview的节点编辑状态,重命名完后,需要关闭节点编辑状态
//右键弹出【重命名(btnRename)】
private void btnRename_Click(object sender, EventArgs e)
{
this.treeView1.LabelEdit = true;
treeView1.SelectedNode.BeginEdit();//设置节点处于可编辑状态
//【不可重命名节点】不可重命名
if (this.treeView1.SelectedNode.Text == "不可重命名节点")
{
MessageBox.Show("该文件不可重命名!");
}
}
//重命名完成后,会触发treeview的AfterLabelEdit()事件
private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
TreeNodeCollection vNodes;//节点集合
if (e.Node.Parent == null) //为根节点
{
vNodes = ((TreeView)sender).Nodes;
}
else //不为根节点,则通过当前的节点的Parent.Nodes来获取同级节点集合
{
vNodes = e.Node.Parent.Nodes;
}
//遍历重命名节点的所有同级节点,如果重命名后出现相同text,就取消重命名操作
foreach (TreeNode vNode in vNodes)
{
//e.node表示重命名前的text,e.Label表示重命名后的text
if ((vNode != e.Node) && (vNode.Text == e.Label))
{
MessageBox.Show("同级存在相同名称,请重命名!");
e.CancelEdit = true;//取消本次重命名,节点恢复重命名前状态
break;
}
}
this.treeView1.SelectedNode.EndEdit(true);//停止退出可编辑状态
}
在获取节点集合前,我们需要了解两种对象①节点集合对象:TreeNodeCollection②节点对象:TreeNode。TreeNodeCollection可多个存储TreeNode对象,TreeNodeCollection就类似于一个treeview控件或其中的某个父节点;TreeNode对象相当于treeview中的任意一个节点,它存储了节点的text、Name、Tag、key、ImageIndex等所有属性信息。
//获取某节点的次级子节点集合对象
TreeNodeCollection tc = this.treeView1.Nodes;//根节点集合
TreeNodeCollection tc = this.treeView1.Nodes;//根节点集合
SetNodeImg(tc);//设置所有子节点的图标
public void SetNodeImg(TreeNodeCollection tc)
{
foreach (TreeNode TNode in tc)
{
//给不同深度的节点,设置不同的图标
//前提:添加imageList控件、给imageList控件加入作为图标的图片、treeview控件的ImageList属性赋值为添加的imageList控件
//设置treeview1的节点图标
this.treeView1.ImageList = this.imageList1;
this.imageList1.ImageSize = new Size(16, 16);
//根据深度赋予图标
TNode.ImageIndex = Convert.ToInt16(TNode.Level);
SetNodeImg(TNode.Nodes);
}
}
teeeview对象与contextMenuStrip控件交互,能够让软件在运行期间,使用者能够实现新建、删除、重命名节点等操作
//新建名称
public static string NodeName;//用于存储新建节点的text
public Form1(string noteName)//在窗体自带函数(Form1下,手动创建一个带参数的Form1()函数)
{
InitializeComponent();
NodeName = noteName;
}
//鼠标右键,点击【新建节点】按钮
private void btnNewNode_Click(object sender, EventArgs e)
{
//实例化frmNewTreeNode窗体对象
frmNewTreeNode f = new frmNewTreeNode();
f.ShowDialog();
if (NodeName != "")
{
//遍历其子级对象,看text中是否已经有了相同名称
foreach (TreeNode TNode in this.treeView1.SelectedNode.Nodes)
{
if (TNode.Text == NodeName)
{
MessageBox.Show("已存在相同名称项目!");
return;
}
}
this.treeView1.SelectedNode.Nodes.Add(new TreeNode(NodeName));
}
}
②新建添加一个Windows窗体对象{Name = frmNewTreeNode }
设置frmNewTreeNode窗体的Load()事件、确定、取消按钮的点击事件
private void frmNewTreeNode_Load(object sender, EventArgs e)
{
//设置界面位于最上方
this.TopMost = true;
//设置界面在显示器最中心、
//激活窗体
this.Activate();
this.StartPosition = FormStartPosition.CenterParent;
}
//【关闭】按钮
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();//关闭窗口
}
//【确认】按钮
private void btnOK_Click(object sender, EventArgs e)
{
//实例化主窗体对象,并将新建节点的名称作为参数传递
Form1 f = new Form1(this.txtNewNodeName.Text);
this.Close();
}
//删除选中的Node
private void btnDel_Click(object sender, EventArgs e)
{
//【不可删除节点】节点不可删除
if (this.treeView1.SelectedNode.Text == "不可删除节点")
{
MessageBox.Show("该节点不可删除!");
return;
}
if (MessageBox.Show("是否删除【" + this.treeView1.SelectedNode.Text + "】节点?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK)//跳出是否删除节点的提示
this.treeView1.SelectedNode.Remove();//确认后删除节点
}
//【重命名节点】按钮
private void btnRename_Click(object sender, EventArgs e)
{
this.treeView1.LabelEdit = true;//开启可编辑状态
treeView1.SelectedNode.BeginEdit();//设置节点处于可编辑状态
//【不可重命名节点】节点不可重命名
if (this.treeView1.SelectedNode.Text == "不可重命名节点")
{
MessageBox.Show("该节点不可重命名!");
}
}
//重命名待确认函数
private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
TreeNodeCollection vNodes;//存储重名节点的同级节点
if (e.Node.Parent == null) //为根节点
{
vNodes = ((TreeView)sender).Nodes;
}
else
{
vNodes = e.Node.Parent.Nodes;
}
foreach (TreeNode vNode in vNodes)
{
if ((vNode != e.Node) && (vNode.Text == e.Label))
{
//e.Node表示重命名节点,其text为节点原本名称;e.Label表示重命名的新名称
MessageBox.Show("同级存在相同名称,请重命名!");
e.CancelEdit = true;//取消重命名
break;
}
}
this.treeView1.SelectedNode.EndEdit(true);//确认重命名
}
由于软件设计需要,我们在软件运行过程中,会对treeview的节点进行新建、删除和重命名等各种操作,然后我们在下次打开软件后,需要与我们上次打开的节点树一致。该功能实现的思路:我们在软件运行程序的目录下,新建一个文本,将每次节点的更新操作都写入该节点信息文本中。下次打开软件后,我们调用该文本,并新建对应treeview树即可。
//软件界面打开
private void From_Load(object sender, EventArgs e)
{
//关闭treeview的text编辑状态
this.treeView1.LabelEdit = false;
//获取应用程序所在文件夹
string AppPath = Application.StartupPath;
//判断数据备份文件夹是否存在,不存在就新建
//判断data文件夹是否存在,不存在就新建
//判断是否存在文件
if (!(Directory.Exists(AppPath + "\\data")))//如果不存在
{
//新建data文件夹,用于保存后续本系统所有文件
Directory.CreateDirectory(AppPath + "\\data") ;
//创建treeview的节点信息
File.Create(AppPath + "\\data\\TreeViewInfo.THB").Close();//该文件用于存储节点信息,文件后缀名随便取,按照文本读取和存储即可
//写入初始化的TreeViewInfo.THB文件
//节点格式:根节点 一级节点 二级节点 三级节点 四级节点 没有则不写,每级两位
//节点内部信息分割(,) 节点之间使用换行符分割(\r\n)
//项目 区间 股道 精调次数 测站文件 空就是00,每个节点用(;)隔开,节点内部数据:节点位置编码、节点名、节点文件位置、节点备注(,)隔开
string DefTreeviewInfo = "0100000000,样例项目,\\data,null\r\n0101000000,样例区间,\\data\\样例项目,null\r\n0101010000,样例轨道,\\data\\样例项目\\样例区间,null\r\n0101010100,第1遍样例精调数据,\\data\\样例项目\\样例区间\\样例轨道,null\r\n0101010200,精调数据合并,\\data\\样例项目\\样例区间\\样例轨道,null\r\n0101010101,20210729.ver,\\data\\样例项目\\样例区间\\样例轨道\\第1遍样例精调数据,null";//初始化内容,用于存储一个样例
//写入
StreamWriter sw = new StreamWriter(AppPath + "\\data\\TreeViewInfo.THB");
sw.Write(DefTreeviewInfo);
sw.Close();
}
TreeViewInfo2CreatTreeViewNode();//通过节点信息文件创建treeview节点树
//treeview1全部展开
this.treeView1.ExpandAll();
}
根据节点信息文本TreeViewInfo.THB中的节点信息,创建节点树
//根据TreeViewInfo.THB文件新建Treeview1的节点树
private void TreeViewInfo2CreatTreeViewNode()
{
//获取应用程序所在文件夹
string AppPath = Application.StartupPath;
//首先读取TreeViewInfo信息,赋值给字符串对象
StreamReader sr = new StreamReader(AppPath + "\\data\\TreeViewInfo.THB");
string TreeViewInfo = sr.ReadToEnd();
sr.Close();
string temp1 = TreeViewInfo.Replace("\r\n",";");
int NodeNum = temp1.Split(';').Length;
//按照节点字符串,将所有节点写入数组
string[] NodesInfo = new string[NodeNum];//用于存储所有节点的信息字符串
for (int i = 0; i < NodeNum; i++)
{
NodesInfo[i] = temp1.Split(';')[i];
}
//根据节点信息数值,创建对应节点树
//使用while循环,如果创建成功,就删除节点对应信息数组
//节点创建必须遵循层次,必须父节点创建才能创建子节点
//创建完的节点,我们从节点集合中剔除,减少后续遍历判断次数
//对于任意N级节点无法用该函数解决,希望大佬提出建议
//创建根节点
foreach (string nodesinfo in NodesInfo)
{
if (nodesinfo == "") continue;
if (Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2)) == 0)//判断是否为根节点,根节点只有前两位有值,后续为0
{
this.treeView1.Nodes.Add(nodesinfo.Split(',')[1]);
this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2)) - 1].Tag = nodesinfo;
List<string> list = NodesInfo.ToList();//先转换为列表
list.RemoveAt(list.IndexOf(nodesinfo));
NodesInfo = list.ToArray();
}
}
//我这里按照节点深度一级一级创建是由于无法定位到指定节点
//新建节点只能使用父节点来完成,所以我们需要先新建上一级节点
//创建一级节点
foreach (string nodesinfo in NodesInfo)
{
if (nodesinfo == "") continue;
if (Convert.ToInt32(nodesinfo.Split(',')[0].Substring(4)) == 0)//判断为一级节点
{
this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2)) - 1].Nodes.Add(nodesinfo.Split(',')[1]);
List<string> list = NodesInfo.ToList();//先转换为列表
list.RemoveAt(list.IndexOf(nodesinfo));
NodesInfo = list.ToArray();
}
}
//创建二级节点
foreach (string nodesinfo in NodesInfo)
{
if (nodesinfo == "") continue;
if (Convert.ToInt32(nodesinfo.Split(',')[0].Substring(6)) == 0)//判断为二级节点
{
//TreeNodeCollection GNode = this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2))].Nodes;
//TreeNodeCollection YNode = GNode[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes;
//YNode.Add(nodesinfo.Split(',')[1]);
this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes.Add(nodesinfo.Split(',')[1]);
List<string> list = NodesInfo.ToList();//先转换为列表
list.RemoveAt(list.IndexOf(nodesinfo));
NodesInfo = list.ToArray();
}
}
//创建三级节点
foreach (string nodesinfo in NodesInfo)
{
if (nodesinfo == "") continue;
if (Convert.ToInt32(nodesinfo.Split(',')[0].Substring(8)) == 0)//判断为三级节点
{
//TreeNodeCollection GNode = this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2))].Nodes;
//TreeNodeCollection YNode = GNode[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes;
//YNode.Add(nodesinfo.Split(',')[1]);
this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(4, 2)) - 1].Nodes.Add(nodesinfo.Split(',')[1]);
List<string> list = NodesInfo.ToList();//先转换为列表
list.RemoveAt(list.IndexOf(nodesinfo));
NodesInfo = list.ToArray();
}
}
//创建四级节点
foreach (string nodesinfo in NodesInfo)
{
if (nodesinfo == "") continue;
if (Convert.ToInt32(nodesinfo.Split(',')[0].Substring(8)) != 0)//判断为四级节点
{
//TreeNodeCollection GNode = this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2))].Nodes;
//TreeNodeCollection YNode = GNode[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes;
//YNode.Add(nodesinfo.Split(',')[1]);
this.treeView1.Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(0, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(4, 2)) - 1].Nodes[Convert.ToInt32(nodesinfo.Split(',')[0].Substring(2, 2)) - 1].Nodes.Add("",nodesinfo.Split(',')[1], 3);
List<string> list = NodesInfo.ToList();//先转换为列表
list.RemoveAt(list.IndexOf(nodesinfo));
NodesInfo = list.ToArray();
}
}
}
//每次节点更新后(treeView1_MouseDown()事件),均要更新节点信息文本
//触发更新节点信息文本事件
private void treeView1_MouseDown(object sender, MouseEventArgs e)
{
//更新TreeViewInfo.THB文件
TreeViewNode2TreeViewInfo();//将当前节点树状态更新到节点信息文本
}
//treeview1的NodeMouseClick事件,触发更新TreeViewInfo.THB文件
private void TreeViewNode2TreeViewInfo()
{
TreeViewInfo = "0100000000,样例项目,\\data,null\r\n0101000000,样例区间,\\data\\样例项目,null\r\n0101010000,样例轨道,\\data\\样例项目\\样例区间,null\r\n0101010100,第1遍样例精调数据,\\data\\样例项目\\样例区间\\样例轨道,null\r\n0101010200,精调数据合并,\\data\\样例项目\\样例区间\\样例轨道,null\r\n0101010101,20210729.ver,\\data\\样例项目\\样例区间\\样例轨道\\第1遍样例精调数据,null\r\n";//初始化的样例节点信息
TreeNodeCollection TC = this.treeView1.Nodes;//获取根节点集合
//获得treeview1对象所有节点
TreeViewInfo = WriteNodeInfo(TC,TreeViewInfo);
//先删除原来的THB文件,再新建写入
File.Delete(Application.StartupPath + "\\data\\TreeViewInfo.THB");
StreamWriter sw = new StreamWriter(Application.StartupPath + "\\data\\TreeViewInfo.THB");
sw.Write(TreeViewInfo);
sw.Close();
return;
}
//遍历递归更新节点信息
public string WriteNodeInfo(TreeNodeCollection tc,string treeViewInfo)
{
//定义节点信息字符串,并加入treeview节点树信息字符串
string TNodeInfo = "";//节点信息字符串
foreach (TreeNode TNode in tc)//遍历传入的所有父节点
{
//定义存储节点编码和节点存储位置的字符串
string nodeID = (TNode.Index + 1).ToString();
//如果为1位,前面补0,如果为两位,直接使用
if (nodeID.Length == 1)
{
nodeID = "0" + nodeID;
}
string nodePath = "";
//判断节点所在层级,给节点编码赋予初始值
for (int i = 0; i < 4 - TNode.Level; i++)//遍历层级数
{
//补全尾部
nodeID += "00";
}
//补全前面部分的id,还用while循环
TreeNode Inode = TNode;//用于存储向上循环查找的父节点
while (Inode.Parent != null)//当是根节点时,停止循环
{
//给父节点补齐0
string StrNodeParentIndex = (Inode.Parent.Index + 1).ToString();
//如果为1位,前面补0,如果为两位,直接使用
if (StrNodeParentIndex.Length == 1)
{
StrNodeParentIndex = "0" + StrNodeParentIndex;
}
nodeID = StrNodeParentIndex + nodeID;//获得当前节点完整编码
nodePath = "\\" + Inode.Parent.Text + nodePath;
Inode = Inode.Parent;//向上遍历父节点
}
TNodeInfo = nodeID + "," + TNode.Text + ",\\data" + nodePath + ",null\r\n";
//如果该节点没有录入
if (!(TreeViewInfo.Contains(TNodeInfo)))
{
TreeViewInfo += TNodeInfo;
}
return TreeViewInfo;
WriteNodeInfo(TNode.Nodes);
}
}