views:

105

answers:

3

My project is WinForms, C#.

I have a form with TreeView with CheckBoxes set to true. There are a few root nodes each having multiple children nodes.

I'd like to have all children nodes to have the same checked/unchecked state as their parent. I wrote the following event handler:

private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
  // return in case the event is induced by the code below
  if (e.Action == TreeViewAction.Unknown)
  {
    return;
  }
  System.Diagnostics.Debug.WriteLine(string.Format("Checked - {0}", e.Node.Checked));
  foreach (TreeNode subNode in e.Node.Nodes)
  {
    subNode.Checked = e.Node.Checked;
  }
}

However, this works quite strange when clicking on the parent quickly. I can easily reproduce this by quickly checking and unchecking parent so that subnodes are all checked while parent is already unchecked.

How to do this more correctly?

+1  A: 

We add a timer that starts/resets when you click. If the timer fire after 500ms the actual update code is executed.

This will fix the fast clicking after each other.

Carra
Thanks - will try. However this amazes me that the second event is never received by the control! This seems to be framework bug.
Alex
Can you please share the code?
Alex
A: 

When you click quickly, the double click event is firing instead of the single click. The single click behavior is what changes the checked status. If you have done something to override the normal double click behavior, make sure to account for that on the child nodes.

msergeant
No, I click slow enough not to receive double click event. I override this event to make sure.
Alex
+1  A: 

This is a bug introduced by the Vista version of the native TreeView control. It responds to a double-click by automatically toggling a check box. But without raising the BeforeCheck and AfterCheck events. The Windows Forms TreeView class wrapper wasn't updated to deal with this problem.

The fix is simple enough, you need to prevent the native control from seeing the double-click. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the existing tree view.

using System;
using System.Windows.Forms;

public class MyTreeView : TreeView {
  protected override void WndProc(ref Message m) {
    // Suppress WM_LBUTTONDBLCLK
    if (m.Msg == 0x203) { m.Result = IntPtr.Zero; }
    else base.WndProc(ref m);
  }
}
Hans Passant
Thanks, Hans! This works perfectly.However, maybe it makes sense to override double click and send AfterCheck event manually? Whan do you think?
Alex
Then your program wouldn't work correctly on XP anymore. It is possible, but you'll have to read the item check state after the base.WndProc call. The P/Invoke is tricky.
Hans Passant