初学者:在 Winforms 中带有复选框和递归的 TreeView

问题描述

如果你需要比我提供的更多信息,那么第一篇文章一个新的编码器就和我一起吧。我正在尝试使用层次结构中的复选框创建树视图(见图)。我的问题是我想创建某种递归,在检查父节点时取消选择和选择子节点,反之亦然。

我正在使用 VS 和 winforms,并在谷歌上搜索了 2 天如何做到这一点,不幸的是,在线示例对我来说要么太先进,要么不起作用。我还找到了关于如何使用不确定的复选框准确执行此操作的 Tutorial,这将是一个很大的好处,但它适用于 WPF。

我设法创建了一些按钮,这些按钮可以通过一些在线示例(取消)检查所有按钮。请有人指导,一个初学者,到目前为止发现编程令人惊叹,朝着正确的方向:)

 private void button_checkAllNodes_Click(object sender,EventArgs e)
    {
        checkAllNodes(treeView1.Nodes);
    }

    private void button_uncheckAllNodes_Click(object sender,EventArgs e)
    {
        UncheckAllNodes(treeView1.Nodes);
    }

    public void checkAllNodes(TreeNodeCollection nodes)
     {
         foreach (TreeNode node in nodes)
         {
             node.Checked = true;
             checkChildren(node,true);
         }
     }
    public void UncheckAllNodes(TreeNodeCollection nodes)
    {
        foreach (TreeNode node in nodes)
        {
            node.Checked = false;
            checkChildren(node,false);
        }
    }

    private void checkChildren(TreeNode rootNode,bool isChecked)
     {
         foreach (TreeNode node in rootNode.Nodes)
         {
             checkChildren(node,isChecked);
             node.Checked = isChecked;
         }
     }

    private void treeView1_AfterSelect(object sender,TreeViewEventArgs e)
    {
        
    }

Picture Treeview with (un)check All buttons

解决方法

让我们为 TreeNode 类型创建几个扩展方法,一个获取节点的所有子节点,另一个获取它的父节点。

// Within your project's namespace...
static class TreeViewExtensions
{
    public static IEnumerable<TreeNode> Children(this TreeNode node)
    {
        foreach (TreeNode n in node.Nodes)
        {
            yield return n;

            foreach (TreeNode child in Children(n))
                yield return child;
        }
    }

    public static IEnumerable<TreeNode> Parents(this TreeNode node)
    {
        var p = node.Parent;

        while (p != null)
        {
            yield return p;

            p = p.Parent;
        }
    }
}

现在,您需要做的就是处理 TreeView.AfterCheck 事件以切换扩展方法产生的节点的 Checked 属性。

// +
using System.Linq;

private void treeView1_AfterCheck(object sender,TreeViewEventArgs e)
{
    if (e.Action == TreeViewAction.Unknown) return;

    foreach (TreeNode n in e.Node.Children())
        n.Checked = e.Node.Checked;

    // Comment this if you don't need it.
    foreach (TreeNode p in e.Node.Parents())
        p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
}

很快,您会注意到,当您快速单击复选框时,有时此解决方案将无法正常工作,因为默认情况下它们不会收到鼠标双击消息。然后,按照this 帖子或this 解决此问题。现在慢慢点击。

如果您更喜欢使用按钮来切换检查状态,请删除 AfterCheck 处理程序并改为执行以下操作:

private void btnCheckAll_Click(object sender,EventArgs e)
{
    ToggleCheck(treeView1.SelectedNode,true);
}

private void btnUncheckAll_Click(object sender,false);
}

private void ToggleCheck(TreeNode node,bool state)
{
    node.Checked = state;

    foreach (TreeNode n in node.Children())
        n.Checked = state;

    // Optional...
    foreach (TreeNode n in node.Parents())
        n.Checked = state;
}
,

我同意@jdweng,您在 checkChildren() 中使用递归。缺少基本情况。 在递归 checkChildren 中,在
之前添加基本情况 foreach (TreeNode node in rootNode.Nodes)
if node is Null : rootNode=isChecked