Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Winforms treeview, recursively check child nodes problem

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?

like image 624
Philip Welch Avatar asked Jul 04 '10 10:07

Philip Welch


2 Answers

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);
    }
}
like image 121
Hans Passant Avatar answered Oct 06 '22 00:10

Hans Passant


Using the solution above, I think it is need to paint more detailed steps, how to apply it for those who want to apply it to an already created TreeView. For example, for me, a beginner, this caused difficulties, but here is the solution:

  1. Creating a class "NoClickTree.cs" in your project.

  2. Include this code in new class:

    public class NoClickTree : TreeView
    {
        protected override void WndProc(ref Message m)
        {
            // Suppress WM_LBUTTONDBLCLK
            if (m.Msg == 0x203) { m.Result = IntPtr.Zero; }
            else base.WndProc(ref m);
        }
    }
    
  3. Go to Form1.Designer.cs or "yourWindowWithTreeView".Designer.cs

  4. Find original initialization at the end of the file, something like private System.Windows.Forms.TreeView treeView;

  5. Replace them on private NoClickTree treeView;

  6. In function private void InitializeComponent() find original initialization, something like this.treeView = new System.Windows.Forms.TreeView();

  7. Replace them on this.treeView = new NoClickTree();

  8. Done!

This steps helped me for solve this problem.

like image 37
Masta Avatar answered Oct 05 '22 22:10

Masta