递归地创建Treeview

时间:2014-12-16 15:38:40

标签: 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

对应的树:

|--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

这是我的代码尝试:

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] + "_";
            }                    
        }
    }
} 

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

result

我怎样才能得到它的回报?即使我一步一步地调试,我也找不到合理的方法。

1 个答案:

答案 0 :(得分:4)

所以我们要做的第一件事就是把我们的字符串转换成树。一旦我们有了树,那么将这些节点映射到TreeView非常容易。

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

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; }
}

简单明了,每个节点都只是一个值和一组孩子。

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

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();
    }
}

现在我们可以获取原始数据,将每个字符串拆分为字符串序列,然后将我们这里的每个节点映射到基于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);