Winforms treeview, recursively check child nodes problem

checkboxtreeviewwinforms

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?

Best Answer

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);
    }
}
Related Topic