将层次结构加载到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}
};
任何想法?
在填充树时,您可以使用字典来检索要添加的新项的父项。如果我们可以假设父项总是在子项之前定义,则第二个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;
}
}
}
希望对你有帮助。