C#中带有复选框的TreeView

7

我有一个带有复选框的树形视图,我希望当用户勾选一个节点时,所有下面级别的节点也自动被勾选。

有没有不用每次用户勾选某个节点都要运行递归函数遍历整个树的方法来实现这个功能呢?

谢谢!

//此函数返回treeView。

   public TreeView GetTreeView()
    {

        getSubject();
        // fill the treeview with all subjects.
        foreach (Subject subject in subjects)
        {
            //for each root subject fill all the his children.
            if (subject.subjestId == subject.parentSubject)
            {
                TreeNode node = new TreeNode(subject.subjectString, subject.subjestId, subject.subjestId);
                addChild(node, subject.subjestId);
                tv.Nodes.Add(node);
            }
        }
        return tv;
    }
   // for each subject return sub subjects.
   private void addChild(TreeNode node, int parentId)
    {
        foreach (Subject subject in subjects)
        {
            if (subject.parentSubject == parentId && subject.parentSubject != subject.subjestId)
            {
                TreeNode childNode = new TreeNode(subject.subjectString, subject.subjestId, subject.subjestId);
                addChild(childNode, subject.subjestId);
                node.Nodes.Add(childNode);
            }
        }
    }

3
@Kiquenet- 这个问题缺少 GUI 平台(WinForms、WPF 和 ASP.NET 都有自己不同的 TreeView)。 - H H
6个回答

18

递归。像这样:

    bool busy = false;

    private void treeView1_AfterCheck(object sender, TreeViewEventArgs e) {
        if (busy) return;
        busy = true;
        try {
            checkNodes(e.Node, e.Node.Checked);
        }
        finally {
            busy = false;
        }
    }

    private void checkNodes(TreeNode node, bool check) {
        foreach (TreeNode child in node.Nodes) {
            child.Checked = check;
            checkNodes(child, check);
        }

出于好奇,为什么要使用“busy”标志?它在代码的其他部分可能有用吗? :) - digEmAll
2
@dig,因为分配child.Checked会导致AfterCheck事件再次运行。那样做不会很好。 - Hans Passant
1
哦,当然!抱歉,我没有注意到if(busy) return;这一行……我非常分心(或许有点困)! :P - digEmAll
不要使用全局布尔开关,可以通过检查 e.Action 属性来忽略“AfterCheck”事件的触发,如果它是“Unknown”,则该事件是由代码而非用户触发的。 - tonycoupland
1
这相当麻烦,通常在设置复选框值的代码中,您希望电视机也保持一致。 - Hans Passant

1
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e) {
  foreach (TreeNode child in e.Node.Nodes) {
    child.Checked = e.Node.Checked;
  }
}

0

这是一个更好的解决方案

private void trvMenuList_AfterCheck(object sender, TreeViewEventArgs e)
        {
            SetChildrenChecked(e.Node, e.Node.Checked);

        }

  private void SetChildrenChecked(TreeNode treeNode, bool checkedState)
        {
            foreach (TreeNode item in treeNode.Nodes)
            {
                if (item.Checked != checkedState)
                {
                    item.Checked = checkedState;
                }
                SetChildrenChecked(item, item.Checked);
            }
        }

0

我稍微扩展了一下答案,同时也更新了父级。[在 catch 语句中的 DisplayException 方法只是一个弹出窗口,我经常使用;你可以自己编写]

    private bool _busy = false;  

    private void treeViewPassFail_AfterCheck(object sender, TreeViewEventArgs e)
    {
        try
        {
            if (_busy)
            {
                return;
            }

            _busy = true;

            CheckNodes(e.Node, e.Node.Checked);

            CheckParent(e.Node.Parent);
        }
        catch(Exception ex)
        {
            DisplayException(ex);
        }
        finally
        {
            _busy = false;
        }
    }

    private void CheckNodes(TreeNode node, bool check)
    {
        foreach(TreeNode child in node.Nodes)
        {
            child.Checked = check;
            CheckNodes(child, check);
        }
    }

    private void CheckParent(TreeNode parent)
    {
        if (parent != null)
        {
            bool allChecked = true;

            foreach (TreeNode node in parent.Nodes)
            {
                allChecked &= node.Checked;
            }

            parent.Checked = allChecked;
        }
    }

0
正如许多答案所述,创建一个递归的“将选中状态设置为子节点”的函数,然后在树上调用它的AfterCheck事件。
不幸的是,即使您在代码中设置了检查值,框架仍会回调到AfterCheck,尽管在小树中可能不会注意到这一点,但会为您的应用程序增加大量指数级的额外工作。为避免这种情况,将AfterCheck过滤,只有在用户触发时才触发您的新函数。
    private void tree_AfterCheck(object sender, TreeViewEventArgs e)
    {
        if (e.Action != TreeViewAction.Unknown)
        {
            SetChildrenChecked(e.Node);
        }
    }

    private void SetChildrenChecked(TreeNode treeNode)
    {
        foreach (TreeNode item in treeNode.Nodes)
        {
            if (item.Checked != treeNode.Checked)
            {
                item.Checked = treeNode.Checked;
            }

            if (item.Nodes.Count > 0)
            {
                SetChildrenChecked(item);
            }                
        }
    }

-1
如果你想在WinForms中完成它,那么我认为你必须通过递归手动完成 - 我不知道有更好的方法。

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