' Databound event handler for a TreeView
Protected Sub TreeView_DataBound(ByVal sender As Object, ByVal e As System.EventArgs)
Dim Tree As TreeView = sender
' Only prune the tree if a node is selected
If Tree.SelectedNode IsNot Nothing Then
Dim childNodes As New Generic.List(Of TreeNode)()
Dim childNodeCollection As New TreeNodeCollection()
FindNodesAtDepth(Tree.Nodes, Tree.SelectedNode.Depth, childNodes)
' Copy from List(of TreeNode) to TreeNodeCollection
For Each node As TreeNode In childNodes
childNodeCollection.Add(node)
Next
' Clear the TreeView nodes and add the nodes we found
Tree.Nodes.Clear()
CloneTreeNodeHierarchyRecursive(childNodeCollection, Tree.Nodes)
End If
End Sub
This function, conceptually, is pretty straight forward. Collect a list of the Nodes at the depth of the selected node, clear the nodes in the tree and add them back into the Tree. However, in practice, the helpers needed make it a bit more complicated.
' Recursively searches the tree for all nodes at a given depth
' and adds them to a list of TreeNodes
Private Sub FindNodesAtDepth(ByVal collection As TreeNodeCollection, _
ByVal targetDepth As Integer, _
ByVal targetNodes As Generic.List(Of TreeNode))
For Each node As TreeNode In collection
If node.Depth = targetDepth Then
targetNodes.Add(node)
ElseIf node.Depth < targetDepth Then
FindNodesAtDepth(node.ChildNodes, targetDepth, targetNodes)
End If
Next
End Sub
There's not much to say about this function, it's pretty simple. It's a depth-first recursive function that creates a list of all the nodes with Depth=targetDepth.
' Recursively creates a clone of a given TreeNode Hierarchy
Private Sub CloneTreeNodeHierarchyRecursive(ByVal inCollection As TreeNodeCollection, _
ByVal outCollection As TreeNodeCollection)
For Each node As TreeNode In inCollection
Dim clonedNode As TreeNode = CType(node, ICloneable).Clone()
outCollection.Add(clonedNode)
If node.ChildNodes.Count > 0 Then
CloneTreeNodeHierarchyRecursive(node.ChildNodes, clonedNode.ChildNodes)
End If
Next
End Sub
This function is interesting and I had some fun writing it. It's also a depth first recursive function. It's job is to create a copy of the hierarchy. Some quick Q&A about this solution:
Q: Why did I copy all the nodes into a list and then to a collection, why not just pass the collection into FindNodesAtDepth()?
A: TreeNodes (and MenuItems) can only live in one collection at a time. When they are added to a collection, they are removed from any collection they previously were in. This causes the initial collection the Node was in to change and .NET does not allow a collection to change while it's being enumerated.
Q: Why did I go through all the business of recursively cloning all the nodes instead of adding them directly to the Tree?
A: This is because, currently, TreeNodes (and MenuItems) cache the Depth property once it is calculated. At this time, there is no straight forward way to tell an entire hierarchy to recalculate their depth except to create a new node which hasn't calculated it's depth. If I hadn't done this, the TreeNodes would actually render themselves deeper than expected.
Link to VB Code Listing
Link to C# Code Listing