17

So I have a TreeView in a C# windows form app. What I need is for some nodes to be "locked" so that they cannot be checked (or unchecked), based on a parameter.

What I am doing now is this:

private void tv_local_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
    TNode node = (TNode)e.Node;
    //if a part node, cancel the action.
    if (node.Type == "Part") {
        e.Cancel = true;     
    }
    //if a locked node, cancel the action
    if (node.Locked == true) {
        e.Cancel = true;
    }
}

This code works great on a single click of the checkbox, but if the user double clicks on a checkbox, it still checks/unchecks.

I have tried playing with the nodeMouseDoubleClick event, but that doesnt really help, since I cannot cancel the event...

Is there any ideas out there how to cancel a double click event on a node?... or anything else? Thanks

svick
  • 236,525
  • 50
  • 385
  • 514
Toadums
  • 2,772
  • 8
  • 44
  • 67
  • Can you post the code for your DoubleClick event handler? – Katie Kilian May 25 '11 at 20:24
  • I do not have anything written there. I dont want anything to happen on a double click... If I could do e.Cancel = true, that would be the code in the doubleclick event – Toadums May 25 '11 at 20:25
  • Are you sure that there aren't any handlers being set anywhere else? I have just tried dropping a tree view onto a form and cannot reproduce the problem (I'm using VSTS2008) – Duncan Howe May 25 '11 at 20:37
  • I managed to reproduce the problem Duncan. If you implement something similar to the questioner, you will find that you will get the check event on the first button down, which you then detect and cancel. However the second button down which is effectively the double click, sets the check but does not fire the event. I'm not sure if this is only a Vista/7 problem, or whether it affects XP as well. – Robinson May 25 '11 at 20:41

4 Answers4

28

This is a bug in the TreeView I think (http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/9d717ce0-ec6b-4758-a357-6bb55591f956/). You need to subclass the tree view and disable the double-click message in order to fix it. Like this:

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

Of course if you do this you'll no longer be able to use the double-click metaphor in the tree-view for other things (such as double click a node to launch a property page, or something).

Robinson
  • 9,666
  • 16
  • 71
  • 115
  • I had seen this solution before, but could not figure out how to make it work - i didnt realize that I had to create a new class and extend the treeview class. Thanks!! – Toadums May 25 '11 at 20:43
  • 3
    Remember that you've to initialize your treeview as a `NoClickTree` in `Form1.Designer.cs` file, not in `Form1.cs`, as explained [here](http://stackoverflow.com/a/14758596/1677209) – T30 Aug 04 '14 at 15:22
6

If you want your double click to actually toggle the check box then try:

protected override void WndProc(ref Message m)
{
  // Filter WM_LBUTTONDBLCLK when we're showing check boxes
  if (m.Msg == 0x203 && CheckBoxes)
  {
    // See if we're over the checkbox. If so then we'll handle the toggling of it ourselves.
    int x = m.LParam.ToInt32() & 0xffff;
    int y = (m.LParam.ToInt32() >> 16) & 0xffff;
    TreeViewHitTestInfo hitTestInfo = HitTest(x, y);

    if (hitTestInfo.Node != null && hitTestInfo.Location == TreeViewHitTestLocations.StateImage)
    {
      OnBeforeCheck(new TreeViewCancelEventArgs(hitTestInfo.Node, false, TreeViewAction.ByMouse));
      hitTestInfo.Node.Checked = !hitTestInfo.Node.Checked;
      OnAfterCheck(new TreeViewEventArgs(hitTestInfo.Node, TreeViewAction.ByMouse));
      m.Result = IntPtr.Zero;
      return;
    }
  }

  base.WndProc(ref m);
}
PhilP
  • 61
  • 1
  • 1
1

I managed it with the following code, which prevents checking root nodes:

private void MyTreeView_MouseUp(object sender, MouseEventArgs e)
{
    // HACK: avoid to check root nodes (mr)
    var node = ((TreeView)sender).GetNodeAt(new Point(e.X, e.Y));
    if (node != null && node.Parent == null)
    BeginInvoke(new MouseEventHandler(TreeView_MouseUpAsync), sender, e);
}

private void TreeView_MouseUpAsync(object sender, MouseEventArgs e)
{
    if (IsDisposed)
        return;

    var node = ((TreeView)sender).GetNodeAt(new Point(e.X, e.Y));
    node.Checked = false;
}
Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
Mikal
  • 11
  • 1
0

Try extending the TreeNode class and add a boolean property that maintains the proper checkedState. That way, when someone double-clicks a node, you can reset the node's checked state back to the value stored in the property. There might be a more elegant solution, but this is the best I can think of.

alexD
  • 2,368
  • 2
  • 32
  • 43