views:

2207

answers:

4

I have the below sub which checks all of a node's child nodes in a Windows form treeview when the node is checked. I get the error stated in the subject whenever I click it, but this only happens when settings the checked property. If I just run the commented out MsgBox line, it works fine with no errors.

Private Sub TreeView1_AfterCheck(ByVal sender As System.Object, ByVal e As _
    TreeViewEventArgs) Handles TreeView1.AfterCheck
    For Each s As TreeNode In TreeView1.Nodes
        If s.Checked = True Then
            For i As Integer = 0 To s.Nodes.Count - 1
                MsgBox(s.Nodes(i).Text)
                s.Nodes(i).Checked = True
            Next
        End If
    Next
End Sub

As it is a StackOverflow Exception, this seemed like the best place to ask about it!

+1  A: 

This line:

s.Nodes(i).Checked = True

causes the event TreeView1_AfterCheck to be triggered itself. So it's an infinite loop.

You need to rewrite the code more carefully, so that you don't re-assign Checked = True if Checked is already true, and perhaps use a private field to check to see if the current event is already running upon entry.

Barry Kelly
+5  A: 

What's happening is that within the AfterCheck event you're 'Checking' a tree node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which fires the AfterCheck event, in which you 'Check' a node, which causes the stack overflow.

To fix, don't set Checked in an AfterCheck event handler.

Will
Funny and clear illustration of the problem. Could have used some formatting though ;o)
Fredrik Mörk
But the final advice is wrong.
Jim Mischel
I feel a bit silly now that I didn't realise that earlier. Very funny illustration of the problem though :)
hermiod
The final advice is not wrong; a bit incomplete, yes, but not wrong. The final advice would definitely fix the issue.
Will
+5  A: 

Documentation for the TreeView.AfterCheck event says:

Setting the TreeNode.Checked property from within a BeforeCheck or AfterCheck event handler causes the event to be raised multiple times and can result in unexpected behavior. To prevent the event from being raised multiple times, add logic to your event handler that only executes your recursive code if the Action property of the TreeViewEventArgs is not set to TreeViewAction.Unknown.

The event is being raised every time you call s.Nodes(i).Checked.

Jim Mischel
this .
Will
Thanks for your answer Jim. I've used a combination of yours and Patrick McDonald's answers as my final solution. Code is in the comment of Patrick's post.
hermiod
A: 

Instead of iterating through all the root nodes of the TreeView, you can simply iterate through the child nodes of the checked node that raised the event:

Private Sub TreeView1_AfterCheck(ByVal sender As System.Object, ByVal e As TreeViewEventArgs) Handles TreeView1.AfterCheck
    If e.Node.Checked Then
        For Each child As TreeNode In e.Node.Nodes
            child.Checked = True
        Next
    End If
End Sub

EDIT:

You don't need to check for e.Action here, and indeed should not, for 2 reasons:

  • As you are enumerating the children of the checked node then you will not have a problem with infinite recursion you were having in your original code.
  • If you exit when e.Action = unknown, then if you have 3 levels in your treeview, it will only select the immediate children of the node you selected, and not all descendants.
Patrick McDonald
Thanks Patrick. Below is the code I settled on using: If e.Action <> TreeViewAction.Unknown Then For Each child As TreeNode In e.Node.Nodes child.Checked = e.Node.Checked Next End IfThis way it automatically works to uncheck all the child nodes as well.Now how do I close a question on this website?
hermiod
Never mind, found it. Who would've though it'd be that extremely obvious big fat tick on the page! :)
hermiod