TreeView中TreeNode.Tag问题

在winform中做了一个TreeView,节点数据存在XML文件里面,现在想用TreeNode节点属性Tag存储些数据,于是将Dictionary对象赋给了treenode.tag,节点级别从高到底依次是root,area,user,lowest,从XML中读出文件的时候节点级别读出都没问题
这是从xml读出文件获取TreeView节点的代码

 private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
        {
            foreach (XmlNode xmlnode in xmlNode)
            {
                if (xmlnode.NodeType != XmlNodeType.Element)    //忽略非Element类型的节点
                {
                    continue;
                }
                TreeNode treenode = new TreeNode();      //新建一个TreeNode,并根据XML节点的Title属性设置文本
                treenode.Text = xmlnode.Attributes["Title"].Value;
               
                if (xmlnode.HasChildNodes)                       //如果XML节点有子节点,则递归调用XmlNode2TreeNode方法转换子节点
                {
                    if (xmlnode.ChildNodes[0].NodeType == XmlNodeType.CDATA)  //如果第一个子节点是CDATA,则将其内容作为TreeNode的Tag
                    {
                        dict["tag"] = xmlnode.ChildNodes[0].Value;
                        treenode.Tag = dict;

                        if (dict["tag"].Equals("root"))
                        {
                            treenode.ImageIndex = 0;
                            treenode.SelectedImageIndex = 0;
                        }
                        else if (dict["tag"].Equals("area"))
                        {
                            treenode.ImageIndex = 1;
                            treenode.SelectedImageIndex = 1;
                        }
                        else if (dict["tag"].Equals("user"))
                        {
                            treenode.ImageIndex = 2;
                            treenode.SelectedImageIndex = 2;
                        }
                        else if (dict["tag"].Equals("lowest"))
                        {
                            treenode.ImageIndex = 2;
                            treenode.SelectedImageIndex = 2;
                        }

                    }
                    //将新建的TreeNode添加到父TreeNode的Nodes 集合中
                    XmlNode2TreeNode(xmlnode.ChildNodes, treenode.Nodes);
                }
                //将新建的TreeNode添加到TreeView的节点集合中
                treeNode.Add(treenode);
            }
        }

这是运行的样子

img

下面是鼠标事件的代码

private void treeView1_MouseUp(object sender, MouseEventArgs e)
        {

            try
            {
                Point mpt = new Point(e.X, e.Y);
                TreeNode TreeClickNode = this.treeView1.GetNodeAt(mpt);

                if (TreeClickNode != null)
                {
                    dict = (Dictionary<string, string>)TreeClickNode.Tag;
                    CreatMenu(dict["tag"]);          //    dict["tag"]里面是node的级别(root,area,user,lowest)
                    //CreatMenu(TreeClickNode1.Tag.ToString());
                    if (e.Button == MouseButtons.Right)
                    {
                        this.contextMenuStrip.Show(this.treeView1, mpt);       //在mpt位置实现treeview1的点击菜单栏
                        //this.treeView1.ContextMenu.Show(treeView1,mpt);
                    }
                }
                else
                {
                     contextMenuStrip.Items.Clear();
                }
            }
            catch
            { 
            
            }
        }

现在右键点击显示菜单的时候一般都只能得到级别都是user,右键点击菜单里面有添加最低一层节点,在添加最低一层节点后点击事件就只能获取到lowset这个级别了,应该是xml读取的地方出问题了但是又找不到是哪里。
如果不将Dictionary赋给TreeNode.tag,而只是用下面的代码,则在点击事件中获取级别没有问题,但是没有Dictionary类型储存数据了,请对这方面熟悉的老兄给解决下吧

private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
        {
            foreach (XmlNode xmlnode in xmlNode)
            {
                if (xmlnode.NodeType != XmlNodeType.Element)    //忽略非Element类型的节点
                {
                    continue;
                }
                TreeNode treenode = new TreeNode();      //新建一个TreeNode,并根据XML节点的Title属性设置文本
                treenode.Text = xmlnode.Attributes["Title"].Value;

                if (xmlnode.HasChildNodes)                       //如果XML节点有子节点,则递归调用XmlNode2TreeNode方法转换子节点
                {
                    if (xmlnode.ChildNodes[0].NodeType == XmlNodeType.CDATA)  //如果第一个子节点是CDATA,则将其内容作为TreeNode的Tag
                    {
                        treenode.Tag = xmlnode.ChildNodes[0].Value;  
                        if (treenode.Tag.ToString().Equals("root"))
                        {
                            treenode.ImageIndex = 0;
                            treenode.SelectedImageIndex = 0;
                            treenode.Expand();
                        }
                        else if (treenode.Tag.ToString().Equals("area"))
                        {
                            treenode.ImageIndex = 1;
                            treenode.SelectedImageIndex = 1;
                           
                        }
                        else if (treenode.Tag.ToString().Equals("user"))
                        {
                            treenode.ImageIndex = 2;
                            treenode.SelectedImageIndex = 2;
                        }
                        else
                        {
                            treenode.ImageIndex = 2;
                            treenode.SelectedImageIndex = 2;
                        }

                    }
                    //将新建的TreeNode添加到父TreeNode的Nodes 集合中
                    XmlNode2TreeNode(xmlnode.ChildNodes, treenode.Nodes);
                }
                //将新建的TreeNode添加到TreeView的节点集合中
                treeNode.Add(treenode);
            }
        }

基于Monster 组和GPT的调写:

  • 问题可能是在XmlNode2TreeNode方法中设置TreeNode的Tag属性时,用了同一个Dictionary实例来保存不同的节点信息。在这个方法中,每个TreeNode都应该有自己的Dictionary实例来保存节点信息。所以,可以在每个循环迭代中,新建一个Dictionary实例,将节点信息存储在其中,然后将这个Dictionary实例赋给TreeNode的Tag属性。

修改后的代码如下:

private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
{
    foreach (XmlNode xmlnode in xmlNode)
    {
        if (xmlnode.NodeType != XmlNodeType.Element)    //忽略非Element类型的节点
        {
            continue;
        }
        TreeNode treenode = new TreeNode();      //新建一个TreeNode,并根据XML节点的Title属性设置文本
        treenode.Text = xmlnode.Attributes["Title"].Value;

        if (xmlnode.HasChildNodes)                       //如果XML节点有子节点,则递归调用XmlNode2TreeNode方法转换子节点
        {
            if (xmlnode.ChildNodes[0].NodeType == XmlNodeType.CDATA)  //如果第一个子节点是CDATA,则将其内容作为TreeNode的Tag
            {
                Dictionary<string, string> dict = new Dictionary<string, string>();
                dict["tag"] = xmlnode.ChildNodes[0].Value;
                treenode.Tag = dict;

                if (dict["tag"].Equals("root"))
                {
                    treenode.ImageIndex = 0;
                    treenode.SelectedImageIndex = 0;
                }
                else if (dict["tag"].Equals("area"))
                {
                    treenode.ImageIndex = 1;
                    treenode.SelectedImageIndex = 1;
                }
                else if (dict["tag"].Equals("user"))
                {
                    treenode.ImageIndex = 2;
                    treenode.SelectedImageIndex = 2;
                }
                else if (dict["tag"].Equals("lowest"))
                {
                    treenode.ImageIndex = 2;
                    treenode.SelectedImageIndex = 2;
                }

            }
            //将新建的TreeNode添加到父TreeNode的Nodes 集合中
            XmlNode2TreeNode(xmlnode.ChildNodes, treenode.Nodes);
        }
        //将新建的TreeNode添加到TreeView的节点集合中
        treeNode.Add(treenode);
    }
}


这样就可以在鼠标事件中正确获取节点的级别了,同时也保留了使用Dictionary实例来存储节点信息的功能。


private void XmlNode2TreeNode(XmlNodeList xmlNodeList, TreeNodeCollection treeNodeCollection)
{
    foreach (XmlNode xmlNode in xmlNodeList)
    {
        if (xmlNode.NodeType != XmlNodeType.Element) continue;
        var treeNode = new TreeNode {Text = xmlNode.Attributes["Title"].Value};

        if (xmlNode.HasChildNodes)
        {
            if (xmlNode.ChildNodes[].NodeType == XmlNodeType.CDATA)
            {
                var tagData = new Dictionary<string, string>
                {
                    ["tag"] = xmlNode.ChildNodes[].Value
                };

                treeNode.Tag = tagData;
            }
            XmlNode2TreeNode(xmlNode.ChildNodes, treeNode.Nodes);
        }
        treeNodeCollection.Add(treeNode);
    }
}

回答引用ChatGPT:
从你的代码中可以看出,在读取XML并将其转换为TreeNode时,你尝试将一个Dictionary<string, string>对象赋给TreeNode的Tag属性。由于TreeNode的Tag属性是object类型,因此你可以将任何对象赋给它。但是,在尝试将一个Dictionary<string, string>对象赋给Tag属性时,你实际上赋给了所有TreeNode节点共同的一个对象,而不是每个节点都有自己的Dictionary对象。

在鼠标事件处理程序中,你尝试获取被点击节点的Tag属性并将其转换为Dictionary<string, string>对象。由于所有的节点共享同一个Dictionary对象,因此你实际上获得的是被最后一个读取的XML节点所对应的Dictionary对象,而不是被点击的节点所对应的Dictionary对象。

解决这个问题的一种方法是,在读取XML并将其转换为TreeNode时,为每个节点创建一个新的Dictionary<string, string>对象,并将该对象赋给TreeNode的Tag属性。修改 XmlNode2TreeNode 方法如下:

private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
{
foreach (XmlNode xmlnode in xmlNode)
{
if (xmlnode.NodeType != XmlNodeType.Element) //忽略非Element类型的节点
{
continue;
}
TreeNode treenode = new TreeNode(); //新建一个TreeNode,并根据XML节点的Title属性设置文本
treenode.Text = xmlnode.Attributes["Title"].Value;
if (xmlnode.HasChildNodes)                       //如果XML节点有子节点,则递归调用XmlNode2TreeNode方法转换子节点
    {
        if (xmlnode.ChildNodes[0].NodeType == XmlNodeType.CDATA)  //如果第一个子节点是CDATA,则将其内容作为TreeNode的Tag
        {
            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict["tag"] = xmlnode.ChildNodes[0].Value;
            treenode.Tag = dict;

            if (dict["tag"].Equals("root"))
            {
                treenode.ImageIndex = 0;
                treenode.SelectedImageIndex = 0;
            }
            else if (dict["tag"].Equals("area"))
            {
                treenode.ImageIndex = 1;
                treenode.SelectedImageIndex = 1;
            }
            else if (dict["tag"].Equals("user"))
            {
                treenode.ImageIndex = 2;
                treenode.SelectedImageIndex = 2;
            }
            else if (dict["tag"].Equals("lowest"))
            {
                treenode.ImageIndex = 2;
                treenode.SelectedImageIndex = 2;
            }

        }
        //将新建的TreeNode添加到父TreeNode的Nodes 集合中
        XmlNode2TreeNode(xmlnode.ChildNodes, treenode.Nodes);
    }
    //将新建的TreeNode添加到TreeView的节点集合中
    treeNode.Add(treenode);
}
}

在鼠标事件处理程序中,你现在可以像下面这样获取被点击节点的Dictionary对象:

private void treeView1_MouseUp(object sender, MouseEventArgs e)
{
try
{
Point mpt = new Point(e.X, e.Y);
TreeNode TreeClickNode = this.treeView1.GetNodeAt(mpt);

获取了被点击的节点后,可以通过 Tag 属性获取该节点的 Dictionary 对象,方法如下:

if (TreeClickNode != null)
{
    Dictionary<string, string> dict = TreeClickNode.Tag as Dictionary<string, string>;
    if (dict != null)
    {
        // 处理获取到的字典对象
    }
}

在上面的代码中,我们首先检查 TreeClickNode 是否为空,以确保用户在单击节点之前没有取消选定任何节点。然后,我们从被点击节点的 Tag 属性中检索出 Dictionary 对象,并将其转换为正确的类型。如果 Tag 属性不包含 Dictionary 对象,则 dict 变量将为 null,并且我们可以相应地处理它。

获取到 dict 对象后,就可以根据需要进行处理了,例如读取其中的键值对、修改字典内容等操作。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先,您使用字典 Dictionary<string, string> dict 存储节点属性,并将其赋值给了 TreeNode.Tag ,您使用字典作为节点属性的目的是为了存储更复杂的节点属性。但是,如果只需存储级别等简单属性,您可以尝试将级别字符串直接赋值给 TreeNode.Tag ,而不使用字典。

其次,您的代码中只在第一个条件分支中为 treenode 分配了 Tag 属性。如果 HasChildNodesfalse ,则 Tag 将保持 null 。您可以将else分支添加到if语句中,并分配空字典来解决该问题。

最后,您的 CreateMenu 方法应该依据传递给它的字符串来动态创建菜单项。您可以在这个方法里处理菜单点击事件。下面是修改过的代码示例:

private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
{
    foreach (XmlNode xmlnode in xmlNode)
    {
        if (xmlnode.NodeType != XmlNodeType.Element)
        {
            continue;
        }

        TreeNode treenode = new TreeNode();
        treenode.Text = xmlnode.Attributes["Title"].Value;
        
        // 添加 else 分支,分配空字典
        if (xmlnode.HasChildNodes)
        {
            if (xmlnode.ChildNodes[0].NodeType == XmlNodeType.CDATA)
            {
                // 将级别字符串赋值给 TreeNode.Tag
                treenode.Tag = xmlnode.ChildNodes[0].Value;

                if (treenode.Tag.ToString().Equals("root"))
                {
                    treenode.ImageIndex = 0;
                    treenode.SelectedImageIndex = 0;
                    treenode.Expand();
                }
                else if (treenode.Tag.ToString().Equals("area"))
                {
                    treenode.ImageIndex = 1;
                    treenode.SelectedImageIndex = 1;
                }
                else if (treenode.Tag.ToString().Equals("user") || treenode.Tag.ToString().Equals("lowest"))
                {
                    treenode.ImageIndex = 2;
                    treenode.SelectedImageIndex = 2;
                }
            }
            else
            {
                // 添加 else 分支,分配空字典
                treenode.Tag = new Dictionary<string, string>();
            }

            XmlNode2TreeNode(xmlnode.ChildNodes, treenode.Nodes);
        }

        treeNode.Add(treenode);
    }
}

private void treeView1_MouseUp(object sender, MouseEventArgs e)
{
    try
    {
        Point mpt = new Point(e.X, e.Y);
        TreeNode TreeClickNode = this.treeView1.GetNodeAt(mpt);

        if (TreeClickNode != null)
        {
            string tag = TreeClickNode.Tag.ToString();
            CreatMenu(tag);
            // 处理菜单项点击事件
        }
        else
        {
            contextMenuStrip.Items.Clear();
        }
    }
    catch
    {
        
    }
}

希望能帮助到您。
如果我的回答解决了您的问题,请采纳!

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:

根据您提供的代码和描述,我认为问题可能是在XmlNode2TreeNode方法中,使用了相同的Dictionary<string, string>对象来为每个TreeNode分配Tag值。由于在分配给TreeNode之前,每个子节点都会将其数据存储在字典中,因此在字典中只会留下最后一次赋值,即最后一个子节点的数据。因此,当您在单击鼠标时检索TreeNode.Tag时,您将获得相同的数据,这将是最后一个子节点的数据。

要解决此问题,您可以为每个子节点创建一个新的Dictionary<string, string>对象,以便在每个子节点之间保留不同的数据。在XmlNode2TreeNode方法中,您可以像下面这样修改代码:
private void XmlNode2TreeNode(XmlNodeList xmlNode, TreeNodeCollection treeNode)
{
foreach (XmlNode xmlnode in xmlNode)
{
if (xmlnode.NodeType != XmlNodeType.Element) //忽略非Element类型的节点
{
continue;
}
    TreeNode treenode = new TreeNode(); //新建一个TreeNode,并根据XML节点的Title属性设置文本
    treenode.Text = xmlnode.Attributes["Title"].Value;

    if (xmlnode.HasChildNodes) //如果XML节点有子节点,则递归调用XmlNode2TreeNode方法转换子节点
    {
        foreach (XmlNode childnode in xmlnode.ChildNodes)
        {
            if (childnode.NodeType == XmlNodeType.Element)
            {
                if (childnode.FirstChild != null && childnode.FirstChild.NodeType == XmlNodeType.CDATA)
                {
                    var dict = new Dictionary<string, string>();
                    dict["tag"] = childnode.FirstChild.Value;
                    var childnode_treenode = new TreeNode();
                    childnode_treenode.Text = childnode.Attributes["Title"].Value;
                    childnode_treenode.Tag = dict;

                    if (dict["tag"].Equals("root"))
                    {
                        childnode_treenode.ImageIndex = 0;
                        childnode_treenode.SelectedImageIndex = 0;
                        childnode_treenode.Expand();
                    }
                    else if (dict["tag"].Equals("area"))
                    {
                        childnode_treenode.ImageIndex = 1;
                        childnode_treenode.SelectedImageIndex = 1;
                    }
                    else if (dict["tag"].Equals("user") || dict["tag"].Equals("lowest"))
                    {
                        childnode_treenode.ImageIndex = 2;
                        childnode_treenode.SelectedImageIndex = 2;
                    }

                    treenode.Nodes.Add(childnode_treenode);
                    XmlNode2TreeNode(childnode.ChildNodes, childnode_treenode.Nodes);
                }
            }
        }
    }
    //将新建的TreeNode添加到TreeView的节点集合中
    treeNode.Add(treenode);
}

}

在这个修改后的代码中,我们遍历每个子节点,并为每个子节点创建一个新的Dictionary<string, string>对象。我们在新的TreeNode实例中分配这些数据,并将该节点添加到其父节点的Nodes集合中。最后,我们递归调用XmlNode2TreeNode方法,以便为每个子节点设置其子节点。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

你可以直接定义TreeNode的key是xpath直接联动起来

代码有点长,如果确有需要,发我完整工程,可代为调试。