views:

51

answers:

3

The following code is taken direct from Microsoft at http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.aftercheck%28VS.80%29.aspx.

  // Updates all child tree nodes recursively.
  private void CheckAllChildNodes(TreeNode treeNode, bool nodeChecked)
  {
   foreach (TreeNode node in treeNode.Nodes)
   {
    node.Checked = nodeChecked;
    if (node.Nodes.Count > 0)
    {
     // If the current node has child nodes, call the CheckAllChildsNodes method recursively.
     this.CheckAllChildNodes(node, nodeChecked);
    }
   }
  }

  // NOTE   This code can be added to the BeforeCheck event handler instead of the AfterCheck event.
  // After a tree node's Checked property is changed, all its child nodes are updated to the same value.
  private void node_AfterCheck(object sender, TreeViewEventArgs e)
  {
   // The code only executes if the user caused the checked state to change.
   if (e.Action != TreeViewAction.Unknown)
   {
    if (e.Node.Nodes.Count > 0)
    {
     /* Calls the CheckAllChildNodes method, passing in the current 
     Checked value of the TreeNode whose checked state changed. */
     this.CheckAllChildNodes(e.Node, e.Node.Checked);
    }
   }
  }

You put it in a form containing a treeview and call node_AfterCheck on (surprise, surprise), the treeview AfterCheck event. It then recursively checks or unchecks the child nodes on the treeview.

However if you actually try it, and click several times on the same treeview check box fast enough, the child nodes end up with their check out-of-sync with the parent. You probably need a couple of levels of children with perhaps 100 children in-total for the UI update to be slow enough to notice this happening.

I've tried a couple of things (such as disabling the treeview control at the beginning of node_AfterCheck and re-enabling at the end), but the out-of-sync problem still happens.

Any ideas?

A: 

Try adding a 5-second thread sleep to your node_AfterCheck method, see whether you're able to change the check state for a second time while it's still running the node_AfterCheck method for the first check state change, and if so whether you eventually receive the corresponding second node_AfterCheck event.

ChrisW
A: 

And if the thread sleep process doesn't do it, if you add a field to the form something like

private Boolean DoingNodeCheck = false;

And then in your AfterCheck event

do the following...

if( YourForm.DoingNodeCheck )
   return;

YourForm.DoingNodeCheck = true;

do your other code

YourForm.DoingNodeCheck = false;

This way, a second click can't accidentally start while you are doing whatever in your code. It will immediately be swallowed and returned back and not disrupt anything else.

DRapp
I very much doubt that the O/S might call the event handler twice concurrently, i.e. reenter it. Instead I wonder whether it's possible that if the event handler is still running (hasn't returned yet) when the second event occurs, then the second event notification might be discarded/lost.
ChrisW
Already tried doing a bool flag as you've suggested (didn't work). Hans Passant's answer below fixed it.
Phil
+1  A: 

The .NET TreeView class heavily customizes mouse handling for the native Windows control in order to synthesize the Before/After events. Unfortunately, they didn't get it quite right. When you start clicking fast, you'll generate double-click messages. The native control responds to a double-click by toggling the checked state for the item, without telling the .NET wrapper about it. You won't get a Before/AfterCheck event.

It's a bug but they won't fix it. The workaround is not difficult, you'll need to prevent the native control from seeing the double-click event. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox, replacing the existing one.

using System;
using System.Windows.Forms;

class MyTreeView : TreeView {
    protected override void WndProc(ref Message m) {
        // Filter WM_LBUTTONDBLCLK
        if (m.Msg != 0x203) base.WndProc(ref m);
    }
}
Hans Passant
Thanks, that works a treat : )I'll register on SO so I can give your answer a + vote.
Phil
How on Earth can you post comments without being registered? There are several older questions that you need to close.
Hans Passant
It just lets me? I went to close the old questions - but you have to pick an option saying why the question was invalid (i.e. wrong forum, off-topic, duplicate). There's no option for 'question was valid but has now been answered'?p.s. thanks again. I would *never* have figured that out.
Phil
Aha! Tick the green arrow!
Phil
There you go, got my first "guru" badge :) +1 requires clicking the up-arrow next to the post. Means "that was helpful". The check mark means "that is the answer". It is common for an answer to be helpful as well. This answer took me half an hour to figure out, in case you're doubting it was helpful.
Hans Passant