拼写错误的单词的 Treeview

Resorting Treeview of misspelled words

我在 C# 中有一个实用程序,它解析源代码并创建一个 xml 报告,其中包含引用的文字字符串中所有拼写错误的单词、文件和它们在文件中出现的位置。我有另一个实用程序读取这个 xml 文件并将其加载到树视图中,它看起来像以下布局:

MisspelledWords
 |
 |___badd
 |    |__Suggestions
 |    |  |__bad
 |    |  
 |    |__Locations
 |       |__Location
 |          |__FileName
 |          |  |__ C:\Workspaces\MyProject\Project1\program.cs
 |          | 
 |          |__LineNumber
 |          |   |
 |          |   |_ 31
 |          |
 |          |__Original Line 
 |   
 |___spellling
 |    |__Suggestions
 |    |  |__spelling
 |    |  
 |    |__Locations
 |       |__Location
 |          |__FileName
 |          |  |__ C:\Workspaces\MyProject\Project1\program.cs
 |          | 
 |          |__LineNumber
 |          |   |
 |          |   |_ 55
 |          |
 |          |__Original Line 

加载到树视图中的所有内容都在第一次成功排序,但是当我清除树视图、重新加载它并排序时 (treeview1.sort),所有子节点都添加到级别上最后一个拼写错误的单词节点1.

这是我当前要加载和排序的代码片段。

private void button1_Click(object sender, EventArgs e)
{
    if (!bTreeLoaded)
    {
        //Add the "Ignored" Top Level node. 
        TreeNode ignoreNode = new TreeNode("Ignored List");
        treeView1.Nodes.Add(ignoreNode);

        XmlDocument xmldoc = new XmlDocument();
        xmldoc.Load(textBox1.Text);
        TreeNode misspelledWordsNode = new TreeNode(xmldoc.DocumentElement.Name);
        treeView1.Nodes.Add(misspelledWordsNode);
        AddNode(xmldoc.DocumentElement, misspelledWordsNode);
        treeView1.Sort();
        bTreeLoaded = true;
    }
    else
    {
        MessageBox.Show("Data has already been loaded");
    }
}

private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
    XmlNode xNode;
    TreeNode tNode;
    XmlNodeList nodeList;
    int i = 0;

    if (inXmlNode.HasChildNodes)
    {
        nodeList = inXmlNode.ChildNodes;
        for (i = 0; i <= nodeList.Count - 1; i++)
        {
            xNode = inXmlNode.ChildNodes[i];
            inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
            tNode = inTreeNode.Nodes[i];
            AddNode(xNode, tNode);
        }
    }
    else
    {
        inTreeNode.Text = (inXmlNode.OuterXml).Trim();
    }
}


private void button2_Click(object sender, EventArgs e)
{
    treeView1.Nodes.Clear();
    bTreeLoaded = false;
}

我的 XML 文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<MisspelledWords xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <badd>
    <Suggestions>
      <Suggestion>bad</Suggestion>
    </Suggestions>
    <Locations>
      <Location>
        <FileName>C:\Workspaces\AHLTA\Current\VB6\global.bas</FileName>
        <LineNumber>31</LineNumber>
        <OriginalLine>s = "badd spellling"</OriginalLine>
      </Location>
    </Locations>
  </badd>
</MisspelledWords>

我无法重现您的问题,因为您没有说明如何在重新加载之前清除预先存在的节点树。在显示的代码中,您只是不允许在树中重新加载节点。

一般来说,如果您想将树更新为一些分层数据,您需要合并每个级别的列表,用节点的名称或标签作为键。例如,以下内容似乎适用于您的 XML:

    private void LoadXML_Click(object sender, EventArgs e)
    {
        treeView1.BeginUpdate();
        try
        {
            //Add the "Ignored" Top Level node. 
            if (!treeView1.Nodes.ContainsKey("Ignored List"))
                treeView1.Nodes.Add(new TreeNode { Name = "Ignored List", Text = "Ignored List" });

            var xmldoc = GetXmlDocument(); // From your UI
            int changed = AddOrMergeNodes(xmldoc);
            Debug.WriteLine("Added or removed " + changed + " nodes");

            if (changed > 0)
            {
                treeView1.ExpandAll();
                treeView1.Sort();
            }
        }
        finally
        {
            treeView1.EndUpdate();
        }

        treeView1.Focus();
    }

    private void clearXML_Click(object sender, EventArgs e)
    {
        treeView1.Nodes.Clear();
    }

    static Dictionary<string, List<TreeNode>> ToNodeDictionary(TreeNodeCollection nodes)
    {
        return nodes.Cast<TreeNode>().Aggregate(new Dictionary<string, List<TreeNode>>(), (d, n) => { d.Add(n.Name, n); return d; });
    }

    int AddOrMergeNodes(XmlDocument xmldoc)
    {
        var dict = ToNodeDictionary(treeView1.Nodes);
        return AddOrMergeNode(treeView1.Nodes, dict, xmldoc.DocumentElement);
    }

    static int AddOrMergeNodes(TreeNodeCollection treeNodeCollection, XmlNodeList xmlNodeList)
    {
        int changed = 0;
        var dict = ToNodeDictionary(treeNodeCollection);
        foreach (XmlNode inXmlNode in xmlNodeList)
        {
            changed += AddOrMergeNode(treeNodeCollection, dict, inXmlNode);
        }
        foreach (var leftover in dict.Values.SelectMany(l => l))
        {
            treeNodeCollection.Remove(leftover);
            changed++;
        }
        return changed;
    }

    static int AddOrMergeNode(TreeNodeCollection treeNodeCollection, Dictionary<string, List<TreeNode>> dict, XmlNode inXmlNode)
    {
        int changed = 0;
        var name = inXmlNode.Name;

        TreeNode node;
        if (!dict.TryRemoveFirst(name, out node))
        {
            node = new TreeNode { Name = name, Text = name };
            treeNodeCollection.Add(node);
            changed++;
        }
        Debug.Assert(treeNodeCollection.Contains(node), "treeNodeCollection.Contains(node)");
        if (inXmlNode.HasChildNodes)
        {
            var text = name;
            if (node.Text != text)
                node.Text = name;
            changed += AddOrMergeNodes(node.Nodes, inXmlNode.ChildNodes);
        }
        else
        {
            var text = (inXmlNode.OuterXml).Trim();
            if (node.Text != text)
                node.Text = text;
            node.Nodes.Clear();
        }
        return changed;
    }

为了方便起见,还有一些处理列表字典的扩展方法:

public static class DictionaryExtensions
{
    public static void Add<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value)
        where TValueList : IList<TValue>, new()
    {
        if (listDictionary == null)
            throw new ArgumentNullException();
        TValueList values;
        if (!listDictionary.TryGetValue(key, out values))
            listDictionary[key] = values = new TValueList();
        values.Add(value);
    }

    public static bool TryGetValue<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, int index, out TValue value)
        where TValueList : IList<TValue>
    {
        TValueList list;
        if (!listDictionary.TryGetValue(key, out list))
            return Returns.False(out value);
        if (index < 0 || index >= list.Count)
            return Returns.False(out value);
        value = list[index];
        return true;
    }

    public static bool TryRemoveFirst<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, out TValue value)
        where TValueList : IList<TValue>
    {
        TValueList list;
        if (!listDictionary.TryGetValue(key, out list))
            return Returns.False(out value);
        var count = list.Count;
        if (count > 0)
        {
            value = list[0];
            list.RemoveAt(0);
            if (--count == 0)
                listDictionary.Remove(key);
            return true;
        }
        else
        {
            listDictionary.Remove(key); // Error?
            return Returns.False(out value);
        }
    }
}

public static class Returns
{
    public static bool False<TValue>(out TValue value)
    {
        value = default(TValue);
        return false;
    }
}