递归地创建Treeview

Tho*_*oub 5 c# recursion

我想在c#中创建一个树视图,它将按前缀对文件进行分组(此处前缀是由分隔符标记的_).以下文件应该给这个树:

Files 清单:

p_a
p_a_test
p_LIG
p_p
p_p_c
p_p_c2
p_p_ccc
p_p_test
p_tres
TestLineGraph1
TestLineGrpah
Run Code Online (Sandbox Code Playgroud)

对应的树:

|--p_
    |--p_a
    |--p_a_test
    |--p_LIG
    |--p_p
    |--p_p_
        |--p_p_c
        |--p_p_c2
        |--p_p_ccc
        |--p_p_test
    |--p_tres
TestLineGraph1
TestLineGrpah
Run Code Online (Sandbox Code Playgroud)

这是我的代码尝试:

private GraphUINode(List<string> subNodes, GraphUINode parent, string name, int lvl = 0)
        : base(parent.m_viewDataSubControl)
{
    parent.Nodes.Add(this);
    this.Name = name;
    this.Text = name;

    string currentPrefix = "";
    int pertinentSubNodes = 0;
    while (pertinentSubNodes < subNodes.Count -1 && subNodes[pertinentSubNodes].Split('_').Length < 2+ lvl)
        pertinentSubNodes++;

    for (int i = 0; i <= lvl; i++)
    {
        currentPrefix += subNodes[pertinentSubNodes].Split('_')[i] + "_";
    }
    List<String> children = new List<string>();
    foreach (string child in subNodes)
    { 
        // The child is in the same group than the previous one
        if (child.StartsWith(currentPrefix))
        {
            children.Add(child);
        }
        else
        {
            // Create a node only if needed
            if (children.Count > 1)
            { 
                 // Create the new node
                new GraphUINode(children, this, currentPrefix, lvl + 1);
                children.Clear();
                children.Add(child);
            }
            else
            {
                new GraphTemplateNode(this, m_viewDataSubControl, child);
            }
            currentPrefix = "";
            for (int i = 0; i <= lvl; i++)
            {
                currentPrefix += child.Split('_')[i] + "_";
            }                    
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

但我在最终结果中遗漏了几个:

结果

我该怎么办呢?即使我一步一步地调试,我找不到合理的方法来做到这一点.

Ser*_*rvy 5

所以我们在这里要做的第一件事就是把我们的字符串变成一棵树。一旦我们有了一棵树,那么将这些节点映射到 aTreeView就很容易了。

我们将从树本身的定义开始:

public class Node<T>
{
    public Node(T value, IEnumerable<Node<T>> children)
    {
        Value = value;
        Children = children;
    }
    public T Value { get; private set; }
    public IEnumerable<Node<T>> Children { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

很好很简单,每个节点只是一个值和一个孩子的集合。

接下来,我们将编写一个方法来获取序列序列,并从中构建一棵树。这里的想法是我们将根据序列中的第一个值对所有项目进行分组,为每个组构建一​​个节点,然后递归调用该组上的方法以获取该节点的子节点。

public static IList<Node<T>> GroupToTree<T>(this IEnumerable<IEnumerable<T>> source)
{
    return GroupToTree(source.Select(sequence => sequence.GetEnumerator()));
}

private static IList<Node<T>> GroupToTree<T>(IEnumerable<IEnumerator<T>> source)
{
    return source.WhereHasNext()
        .GroupBy(iterator => iterator.Current)
        .Select(group => new Node<T>(group.Key, GroupToTree(group)))
        .ToList();
}

//This ensures that the iterators all get disposed
private static IEnumerable<IEnumerator<T>> WhereHasNext<T>(
    this IEnumerable<IEnumerator<T>> source)
{
    foreach (var iterator in source)
    {
        if (iterator.MoveNext())
            yield return iterator;
        else
            iterator.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以获取原始数据,将每个字符串拆分为字符串序列,然后将这里的每个节点映射到基于 UI 的节点进行展示:

List<string> rawData = new List<string>();
//TODO populate raw data
Func<Node<string>, TreeNode> selector = null;
selector = node => new TreeNode(node.Value, node.Children.Select(selector).ToArray());
var nodes = rawData.Select(line => line.Split('_').AsEnumerable())
    .GroupToTree()
    .Select(selector);
Run Code Online (Sandbox Code Playgroud)