将层次结构加载到树视图

时间:2015-09-23 07:49:14

标签: c# winforms treeview hierarchy

我得到了这个课程:

public class hierarchymain 
   {
       public String label;              // Label to be displayed in treeview
       public int parentId;             // ID of the parent. Root have 0 in parent ID
       public int ID;                     //Id in table. ignore it
   }

但我无法找到将其加载到树视图中的正确方法......

hierarchy = new List<Hierarchymain> {
            new Hierarchymain{Label="ONE",ParentID=0, ID=0},
            new Hierarchymain{Label="TWO", ParentID=0, ID=1},
            new Hierarchymain{Label="THREE", ParentID=1, ID=2},
            new Hierarchymain{Label="Four", ParentID=2, ID=3},
            new Hierarchymain{Label="five", ParentID=3, ID=4},
            new Hierarchymain{Label="six", ParentID=4, ID=5},
            new Hierarchymain{Label="seven", ParentID=0, ID=6}
        };

任何想法?

2 个答案:

答案 0 :(得分:2)

填充树时,您可以使用字典检索要添加的新项目的父项。如果我们可以假设父母总是在孩子面前定义,那么第二个foreach可以省略。

var dict = new Dictionary<int, TreeNode>();
var orphans = new Queue<Hierarchymain>();
foreach (Hierarchymain item in hierarchy)
{
    TreeNode newNode = new TreeNode(item.Label);
    TreeNode parent;
    if (dict.TryGetValue(item.ParentID, out parent))
        parent.Nodes.Add(newNode);
    else if (item.ParentID == 0)
        treeView1.Nodes.Add(newNode);
    else
        orphans.Enqueue(item);

    dict.Add(item.ID, newNode);
}

// processing nodes, whose parents were created later
foreach (Hierarchymain item in orphans)
{
    TreeNode orphan = dict[item.ID];
    TreeNode parent;
    if (dict.TryGetValue(item.ParentID, out parent))
        parent.Nodes.Add(orphan); // parent found
    else
        treeView1.Nodes.Add(orphan); // the node is a real orphan, adding to the root
}

答案 1 :(得分:1)

有很多方法可以做到这一点。在我开始之前,我想指出,正如许多评论者提到的那样,提供的示例不正确 - 如果ParentID=0表示根节点,那么您不应该有ID=0的节点,所以我&# 39;将使用与您相同的数据,但每个项目ID都会增加。以下是我将用于测试的更正(并且另外改组)的数据:

var source = new List<Hierarchymain> {
    new Hierarchymain{ Label="THREE", ParentID=2, ID=3},
    new Hierarchymain{ Label="six", ParentID=5, ID=6},
    new Hierarchymain{ Label="Four", ParentID=3, ID=4},
    new Hierarchymain{ Label="five", ParentID=4, ID=5},
    new Hierarchymain{ Label="TWO", ParentID=1, ID=2},
    new Hierarchymain{ Label="seven", ParentID=0, ID=7},
    new Hierarchymain{ Label="ONE", ParentID=0, ID=1},
};

现在的解决方案。

(A)简单的递归实现:从根项开始,为每个项创建一个节点,然后对其子项执行相同的操作。

static void PopulateA(TreeView treeView, IEnumerable<Hierarchymain> source)
{
    AddNodes(treeView.Nodes, 0, source);
}
static void AddNodes(TreeNodeCollection parentNodes, int parentID, IEnumerable<Hierarchymain> source)
{
    foreach (var item in source.Where(item => item.ParentID == parentID))
    {
        var node = parentNodes.Add(item.Label);
        AddNodes(node.Nodes, item.ID, source);
    }
}

(B)(A)的不同实施。 (A)的问题在于它具有O(N ^ 2)时间复杂度。但是,通过在开头准备查找结构并保持简单性,可以很容易地改进这一点。

static void PopulateB(TreeView treeView, IEnumerable<Hierarchymain> source)
{
    AddNodes(treeView.Nodes, 0, source.ToLookup(item => item.ParentID));
}
static void AddNodes(TreeNodeCollection parentNodes, int parentID, ILookup<int, Hierarchymain> source)
{
    foreach (var item in source[parentID])
    {
        var node = parentNodes.Add(item.Label);
        AddNodes(node.Nodes, item.ID, source);
    }
}

(C)上述迭代实现。如果层次结构太深,则递归实现将失败并显示StackOverflowException。这可以通过将它们变成迭代版本来解决,牺牲了可读性和简单性。这相当于(B):

static void PopulateC(TreeView treeView, IEnumerable<Hierarchymain> source)
{
    var itemsByParentId = source.ToLookup(item => item.ParentID);
    var parentNodesStack = new Stack<TreeNodeCollection>();
    var childEnumeratorStack = new Stack<IEnumerator<Hierarchymain>>();
    var parentNodes = treeView.Nodes;
    var childEnumerator = itemsByParentId[0].GetEnumerator();
    try {
        while (true)
        {
            while (childEnumerator.MoveNext())
            {
                var item = childEnumerator.Current;
                var node = parentNodes.Add(item.Label);
                parentNodesStack.Push(parentNodes);
                childEnumeratorStack.Push(childEnumerator);
                parentNodes = node.Nodes;
                childEnumerator = itemsByParentId[item.ID].GetEnumerator();
            }
            if (childEnumeratorStack.Count == 0) break;
            childEnumerator.Dispose();
            childEnumerator = childEnumeratorStack.Pop();
            parentNodes = parentNodesStack.Pop();
        }
    }
    finally
    {
        childEnumerator.Dispose();
        while (childEnumeratorStack.Count != 0) childEnumeratorStack.Pop().Dispose();
    }
}

(D)这是我经常使用的算法。同样的想法可以递归地实现,但我使用它是出于与(C)中提到的相同的原因。它的工作原理如下:在处理过程中,我们按ID维护已创建节点的映射,以便能够定位父节点。对于输入序列中的每个项目,我们构建一个堆栈,其中包含尚未创建节点的项目,然后以相反的顺序处理它。这样我们就可以确保父母在孩子面前创造。

static void PopulateD(TreeView treeView, IEnumerable<Hierarchymain> source)
{
    var itemById = source.ToDictionary(item => item.ID);
    var nodeById = new Dictionary<int, TreeNode>();
    var addStack = new Stack<Hierarchymain>();
    foreach (var nextItem in source)
    {
        // Collect the info about the nodes that need to be created and where
        TreeNodeCollection parentNodes;
        for (var item = nextItem; ; item = itemById[item.ParentID])
        {
            TreeNode node;
            if (nodeById.TryGetValue(item.ID, out node))
            {
                // Already processed
                parentNodes = node.Nodes;
                break;
            }
            addStack.Push(item);
            if (item.ParentID == 0)
            {
                // Root item
                parentNodes = treeView.Nodes;
                break;
            }
        }
        // Create node for each collected item in reverse order
        while (addStack.Count > 0)
        {
            var item = addStack.Pop();
            var node = parentNodes.Add(item.Label);
            nodeById.Add(item.ID, node);
            parentNodes = node.Nodes;
        }
    }
}

希望有所帮助。