Reputation: 19574
Suppose I have a WinForms Treeview that looks as follows:
Parent1
Child1
Sub-Child1
DeepestNode1
DeepestNode2
DeepestNode3
Sub-Child2
DeepestNode4
DeepestNode5
DeepestNode6
Child2
Sub-Child3
Sub-Child4
Sub-Child5
Sub-Child6
Child3
(no children)
I would like to create a function along the lines of:
Function GetDeepestChildren(MyNode as Treenode) as List(Of Treenode)
Where, if the results would look like:
GetDeepestChildren(Parent1) = {DeepestNode1, DeepestNode2, DeepestNode3, DeepestNode4, DeepestNode5, DeepestNode6}
GetDeepestChildren(Sub-Child1) = {DeepestNode1, DeepestNode2, DeepestNode3}
GetDeepestChildren(Child2) = {Sub-Child3, Sub-Child4, Sub-Child5, Sub-Child6}
GetDeepestChildren(Child3) = Empty list
... In other words, always go to the deepest level you can from the node given and return the children - Even if they're split between different parents (as was the case in Parent1
).
I have created a function that will tell me how many levels deeper a node goes that looks like:
Public Function GetDeepestChildNodeLevel(ByVal ParentNode As TreeNode) As Integer
Dim subLevel = ParentNode.Nodes.Cast(Of TreeNode).Select(Function(subNode) GetDeepestChildNodeLevel(subNode))
Return If(subLevel.Count = 0, 0, subLevel.Max() + 1)
End Function
So I know from what level to get the children, what i'm looking for is a function that can do this - Somethign along the lines of:
Function GetDeepestChildren(MyNode as Treenode) as List(Of Treenode)
Return All child nodes where level = GetDeepestChildNodeLevel(MyNode)
End function
I hope this makes sense - Thanks!
Upvotes: 5
Views: 7459
Reputation: 19574
This is a VB.Net re-make I created of @dasblinkenlight's solution - It worked perfectly and I'm just putting it here in case anyone in the future needs the solution in VB.
Public Function GetDeepestChildNodes(ByVal ParentNode As TreeNode) As List(Of TreeNode)
Dim RetVal As New List(Of TreeNode)
If ParentNode.Nodes.Count > 0 Then
RetVal = (From nd As TreeNode In ParentNode.Nodes
Where nd.Nodes.Count = 0
Select nd).ToList
For Each nd In ParentNode.Nodes
RetVal.AddRange(GetDeepestChildNodes(nd))
Next
End If
Return RetVal
End Function
Thank you all again for your help!!!
Upvotes: 1
Reputation: 70538
Here is a version using XML -- the translation should be easy. I used linqPad which I recommend for this kind of stuff, you can run this and see it work directly in linkPad
WalkDeep(tree,getDeep(tree)) returns:
<DeepestNode1 />
<DeepestNode2 />
<DeepestNode3 />
<DeepestNode4 />
<DeepestNode5 />
<DeepestNode6 />
The C# code is nicer because you can use yield
VB Code
function getDeep( e as XElement) as integer
if (e.HasElements)
return 1 + e.Elements().Select(Function(c) getDeep(c)).Max()
else
return 1
end if
end function
function WalkDeep(root as XElement,find as integer,optional mylevel as integer = 1) as IEnumerable(of XElement)
Dim result As New List(Of XElement)
if find = mylevel
result.Add(root)
else
if root.HasElements
for each c as XElement in root.Elements()
for each r as XElement in WalkDeep(c,find,mylevel+1)
result.Add(r)
next
next
end if
end if
return result
end function
Sub Main
dim tree as XElement = <Parent1>
<Child1>
<Sub-Child1>
<DeepestNode1/>
<DeepestNode2/>
<DeepestNode3/>
</Sub-Child1>
<Sub-Child2>
<DeepestNode4/>
<DeepestNode5/>
<DeepestNode6/>
</Sub-Child2>
</Child1>
<Child2>
<Sub-Child3/>
<Sub-Child4/>
<Sub-Child5/>
<Sub-Child6/>
</Child2>
<Child3 />
</Parent1>
WalkDeep(tree,getDeep(tree)).Select(function(x) x.Name.LocalName).Dump()
End Sub
C# Code:
int getDeep(XElement e)
{
if (e.HasElements)
return 1 + e.Elements().Select(c => getDeep(c)).Max();
else
return 1;
}
IEnumerable<XElement> WalkDeep(XElement root,int find, int mylevel=1)
{
if (find == mylevel) yield return root;
if (root.HasElements)
{
foreach(XElement c in root.Elements())
{
foreach(XElement r in WalkDeep(c,find,mylevel+1))
yield return r;
}
}
yield break;
}
void Main()
{
XElement tree = XElement.Parse (@"
<Parent1>
<Child1>
<Sub-Child1>
<DeepestNode1/>
<DeepestNode2/>
<DeepestNode3/>
</Sub-Child1>
<Sub-Child2>
<DeepestNode4/>
<DeepestNode5/>
<DeepestNode6/>
</Sub-Child2>
</Child1>
<Child2>
<Sub-Child3/>
<Sub-Child4/>
<Sub-Child5/>
<Sub-Child6/>
</Child2>
<Child3 />
</Parent1>
");
WalkDeep(tree,getDeep(tree)).Dump();
}
Upvotes: 1
Reputation: 20120
it is not linq, since i think in this case it should not be done by linq sorry if it's not what you asked but this work. it is not fullproof but at least you wont get stackoverflow if you get a crazy tree
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim test1 = GetDeepestChildren(TreeView1.Nodes(0))
Dim test2 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(0).Nodes(0))
Dim test3 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(1))
Dim test4 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(2))
End Sub
Private Function GetDeepestChildren(ByVal node As TreeNode) As List(Of TreeNode)
Dim deepestList As New List(Of TreeNode)
If node.Nodes.Count = 0 Then
Return deepestList
End If
Dim nodes As New Stack(Of TreeNode)
For Each n As TreeNode In node.Nodes
nodes.Push(n)
Next
Dim deepest As Integer = 0
Do Until nodes.Count = 0
node = nodes.Pop
If node.Nodes.Count = 0 Then
If deepest < node.Level Then
deepest = node.Level
deepestList.Clear()
deepestList.Add(node)
ElseIf deepest = node.Level Then
deepestList.Add(node)
End If
Else
For Each n As TreeNode In node.Nodes
nodes.Push(n)
Next
End If
Loop
Return deepestList
End Function
Upvotes: 0
Reputation: 16934
This is just a slight modification of @dasblinkenlight 's answer, so don't upvote this!
Just a matter of personal style, but I rather like the recursive call as such:
IEnumerable<TreeNode> WalkNodes(TreeNode root)
{
yield return root;
var children = root.Nodes.Cast<TreeNode>();
foreach (var child in children)
{
foreach(var subChild in WalkNodes(child))
{
yield return subChild;
}
}
}
And called via:
foreach (var node in treeView.Nodes.Cast<TreeNode>())
{
var walkedFrom = WalkNodes(node);
foreach (var subNode in walkedFrom)
{
Console.WriteLine(subNode.Text);
}
}
Upvotes: 0
Reputation: 726839
In C# you can do it with yield return
or with a recursive lambda. Here is an example of the second approach:
Func<TreeNode,IEnumerable<TreeNode>> getChildren = null;
getChildren = n => {
if (n.Nodes.Count != 0) {
var list = new List<TreeNode>(n.Nodes.Where(c => c.Nodes.Count == 0));
foreach (var c in n.Nodes) {
// Note the recursive call below:
list.AddRange(getChildren(c));
}
return list;
} else {
return new TreeNode[0];
}
};
var res = getChildren(myTree);
Upvotes: 4
Reputation: 1256
I'm not familiar with the TreeView control, but is the Level property of the TreeNode any good to you?
http://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.level.aspx
If you know the deepest level, you could do this:
C#
private List<TreeNode> GetDeepestChildren(int level)
{
return (from p in treeView1.Nodes.Cast<TreeNode>() where p.Level == level select p).ToList();
}
VB
Private Function GetDeepestChildren(level As Integer) As List(Of TreeNode)
Return (From p In treeView1.Nodes.Cast(Of TreeNode)() Where p.Level = levelp).ToList()
End Function
Greg.
Upvotes: 0
Reputation: 11055
Tried a recursive function yet? Not sure in VB.Net but C# would look like
public List<TreeNode> GetDeepestChildren(Treenode MyNode)
{
if (MyNode.Children.Count > 0)
GetDeepestChildren(Treenode MyNode);
else
return MyNode.Children;
}
This hasnt been compiled or tested but the idea is there and should return the deepest children for any given node.
Upvotes: 0