将层次结构加载到treeview

本文关键字:treeview 加载 层次结构 | 更新日期: 2023-09-27 18:14:10

我得到了这门课:

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
   }

但是我想不出正确的方法来加载它到treeview…

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

任何想法?

将层次结构加载到treeview

在填充树时,您可以使用字典来检索要添加的新项的父项。如果我们可以假设父项总是在子项之前定义,则第二个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
}

有很多方法可以做到。在我离开之前,我想指出,正如许多评论者提到的,所提供的样本是不正确的——如果ParentID=0代表一个根节点,那么你不应该有一个ID=0的节点,所以我将使用与你相同的数据,但每个项目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;
        }
    }
}

希望对你有帮助。