Winform编程中的treeview控件


treeview作为一个树状控件,在管理各种子功能或数据模块时,使用非常方便。treeview控件的操作一般包含节点的添加、删除、节点重命名、节点的展开与折叠、节点设置图标,更高级一点的操作包括treeview与文件的互操作、节点与右键弹出窗口的互操作。 以下内容是本人第一个博客,对学习研究过程进行记录,有何问题欢迎各位批评指正。

treeview基础操作

treeview添加、删除节点操作

  • 手动设置控件节点
    在treeview控件上鼠标右键,弹出框选中【编辑节点…】,即可进行treeview节点的各种设置与操作。
    在这里插入图片描述* 代码添加节点
    treeview只能通过父节点(根节点)的.Nodes对象(treeview.Nodes)添加子节点。
// 添加根节点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));
  • 代码删除节点
    treeview使用节点的Remove()方法删除节点,我们可以通过各种判断(text、key、level、name…),查看是否为我们需要删除的节点,然后确认是否删除
//指定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节点展开与折叠

当treeview节点对象过多时,我们为了查找节点更为方便,需要使用button来控制节点的展开与折叠

//展开节点
//treeview1所有节点全部展开
this.treeView1.ExpandAll();
//展开选中节点的下一级子节点
this.treeView1.SelectedNode.Expand();
//展开选中节点的所有子节点(包括以下多个层级所有子节点)
this.treeView1.SelectedNode.ExpandAll();
//折叠节点
this.treeView1.CollapseAll();//折叠treeview所有节点
this.treeView1.SelectedNode.CollapseAll();//折叠选中节点下的所有节点

treeview节点重命名

节点重命名操作,首先需要进入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);//停止退出可编辑状态
}

treeview获取节点集合并设置节点图标

在获取节点集合前,我们需要了解两种对象①节点集合对象:TreeNodeCollection②节点对象:TreeNode。TreeNodeCollection可多个存储TreeNode对象,TreeNodeCollection就类似于一个treeview控件或其中的某个父节点;TreeNode对象相当于treeview中的任意一个节点,它存储了节点的text、Name、Tag、key、ImageIndex等所有属性信息。

  • 获取某节点的次级节点集合
    节点的.Nodes对象即为其次级节点集合
//获取某节点的次级子节点集合对象
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);
    }
}

treeview与contextMenuStrip控件

teeeview对象与contextMenuStrip控件交互,能够让软件在运行期间,使用者能够实现新建、删除、重命名节点等操作

  • 添加节点
    ①contextMenuStrip控件新建一个button{Name = btnNewNode;Text = 新建节点},实例化private void btnNewNode_Click(object sender, EventArgs e)事件
//新建名称
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();
}
  • 删除节点
    contextMenuStrip控件新建一个button{Name = btnDel;Text = 删除节点},实例化private void btnDel_Click(object sender, EventArgs e)事件
//删除选中的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();//确认后删除节点
}
  • 重命名节点
    contextMenuStrip控件新建一个button{Name = btnRename;Text = 重命名节点},实例化private void btnRename_Click(object sender, EventArgs e)和private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)事件
//【重命名节点】按钮
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的节点进行新建、删除和重命名等各种操作,然后我们在下次打开软件后,需要与我们上次打开的节点树一致。该功能实现的思路:我们在软件运行程序的目录下,新建一个文本,将每次节点的更新操作都写入该节点信息文本中。下次打开软件后,我们调用该文本,并新建对应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);
    }
}
  • 15
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ping_Kingzero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值