Winforms 树形视图,递归检查子节点问题

17

以下代码直接来自于Microsoft,网址为 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);
    }
   }
  }

在一个包含树形视图的表单中,您调用了treeview的AfterCheck事件上的node_AfterCheck函数。然后,它递归地检查或取消选中treeview上的子节点。

但是,如果您实际尝试并快速多次单击同一棵树的复选框,则子节点最终会导致其选中状态与父节点不同步。您可能需要几层子级,其中每个子级可能包含100个子项,以使UI更新足够缓慢,才能注意到此问题。

我已经尝试过一些方法(例如,在node_AfterCheck开头禁用treeview控件,并在结束时重新启用),但是不同步的问题仍然发生。

有任何想法吗?


我已经在这个主题的子主题中发布了我的解决方法:https://dev59.com/VW7Xa4cB1Zd3GeqPlhL2#23065225 - Luiz Oliveira
2个回答

39

为了合成Before/After事件,.NET TreeView类强烈自定义了本机Windows控件的鼠标处理。 不幸的是,他们并没有做得很完美。当您开始快速点击时,会生成双击消息。本机控件通过切换项目的已选状态来响应双击,但没有告诉.NET封装器。 因此,您将无法获取Before/AfterCheck事件。

这是一个错误,但他们不会修复它。解决方法并不难,您需要防止本机控件看到双击事件。将下面的代码粘贴到新类中,并将其编译为程序集。从工具箱的顶部拖放新控件,以替换现有控件。

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

2
为什么他们不修复它? - Kamil
@Kamil 因为 winforms 即将死亡。他们迟早会转向 WPF - King King
我对此没意见,但我对微软的发展方向感到不满。奇怪的Windows 8、奇怪的Windows Phone和奇怪的VS2012。虽然界面更漂亮了,但是他们忽视了使用人性化这一点。 - Kamil
一个解决方法并不难,除非这个复选框是在Internet Explorer中的。 - Stefan Steiger
2
2017年报道,感谢这个解决方法,WinForms还没有死。 - Dmitry Sadakov
显示剩余2条评论

1
使用上述解决方案,我认为需要更详细地说明如何将其应用于已创建的TreeView,以便那些想要应用它的人能够理解。例如,对于像我这样的初学者,这会带来困难,但是这里有解决方案:
  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!

这些步骤帮助我解决了这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接