Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Treeview recursively

Tags:

c#

recursion

I want to create a treeview in c# which will group file by prefix (here the prefix is a marked by the separator _). The following files should give this tree:

Files list :

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

Corresponding tree:

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

Here's my attempt of code:

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

But I miss a few ones in the final result:

result

How can I get its back? Even when I debug step by step I can't find the logical way to do it.

like image 537
Thomas Ayoub Avatar asked Dec 16 '14 15:12

Thomas Ayoub


1 Answers

So the first thing that we'll want to do here is take our strings and turn them into a tree. Once we have a tree then mapping those nodes to a TreeView is quite easy.

We'll start out with the definition for the tree itself:

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

Nice and easy, each node is just a value and a collection of children.

Next we'll write a method to take a sequence of sequences, and build a tree from it. The idea here is that we'll group all of the items based on the first value in their sequence, build a node for each group, and then recursively call the method on the group to get the children for that node.

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

Now we can take the raw data, split each of the strings into sequences of strings, and then map each of the nodes that we have here into UI-based nodes for presentation:

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);
like image 153
Servy Avatar answered Sep 30 '22 11:09

Servy