【问题标题】:Trying to set all nodes checked status in a treeview - get StackOverflowException尝试在树视图中设置所有节点的检查状态 - 获取 StackOverflowException
【发布时间】:2017-02-20 13:45:11
【问题描述】:

我的TreeView (trvP) 添加了一些节点。根元素的Tag 属性设置为Root

我正在努力做到这一点,所以如果我检查根元素,所有其他节点都将具有相同的状态。但是,执行下面的代码会导致StackOverflowException

private void trvP_AfterCheck(object sender, TreeViewEventArgs e)
{
        if(e.Node.Tag.Equals("Root"))
        {
            var nodes = TreeViewExtensions.GetAllNodes(e.Node.TreeView);
            foreach (TreeNode node in nodes)
                node.Checked = e.Node.Checked;
        }
}

GetAllNodes函数的代码:

public static List<TreeNode> GetAllNodes(this TreeView _trv)
{
        List<TreeNode> result = new List<TreeNode>();
        foreach (TreeNode child in _trv.Nodes)
        {
            result.AddRange(child.GetAllNodes());
        }
        return result;
}
public static List<TreeNode> GetAllNodes(this TreeNode _trn)
    {
        List<TreeNode> result = new List<TreeNode>();
        result.Add(_trn);
        foreach (TreeNode child in _trn.Nodes)
        {
            result.AddRange(child.GetAllNodes());
        }
        return result;
    }

从调试来看,它似乎一遍又一遍地运行var nodes = ... 代码,而只在foreach 循环(根节点)内设置单个节点。但是,nodes 是具有正确值的节点的正确列表。

我不明白为什么一遍又一遍地执行导致异常。

【问题讨论】:

  • 你能显示TreeNode的扩展名GetAllNodes()吗(你只显示TreeView的扩展名)?
  • 我猜根节点将是您列表中的第一个,因此node.Checked = ... 将再次为同一个节点调用处理程序trvP_AfterCheck()(尽管如果检查状态没有改变,这不应该发生)。
  • 已添加,但如下所述,如果我将GetAllNodes 称为一些随机子元素(也有子元素),它可以正常工作 - 它只是导致问题的根元素。

标签: c# winforms treeview


【解决方案1】:

TreeViewGetAllNodes() 扩展从第一个子节点开始,并为此 TreeNode 调用 GetAllNodes() 扩展。

这个GetAllNodes() 扩展将这个TreeNode 添加到结果列表中。
所以你列表中的第一个TreeNode又是你的根节点

这意味着在行中

node.Checked = e.Node.Checked;

您设置了 根节点Checked 属性,该属性又调用处理程序 trvP_AfterCheck 再次为此根节点。现在这会无限重复,淹没你的筹码并提高StackOverflowException

为了解决这个问题,过滤掉根节点:

private void trvP_AfterCheck(object sender, TreeViewEventArgs e)
{
        if(e.Node.Tag.Equals("Root"))
        {
            var nodes = e.Node.TreeView.GetAllNodes();
            foreach (TreeNode node in nodes)
            {
                if (node == e.Node) continue; // don't do it for root again
                node.Checked = e.Node.Checked;
            }
        }
}

顺便说一句:扩展方法的好处是您可以像调用实例方法一样在语法上调用它们。所以这个

var nodes = TreeViewExtensions.GetAllNodes(e.Node.TreeView)

可以写成

var nodes = e.Node.TreeView.GetAllNodes();

【讨论】:

  • 我不认为一个完整的列表会一遍又一遍地引发事件,这是一个很好的解释,谢谢。
【解决方案2】:

您正在递归调用 GetAllNodes,这会使循环进入无限状态..

试试

    public static List<TreeNode> GetAllNodes(this TreeView _trv)
{
        List<TreeNode> result = new List<TreeNode>();
        foreach (TreeNode child in _trv.Nodes)
        {
            result.Add(child);
        }
        return result;
}

当你已经将treenode循环到_trv.Nodes中时,你只需要将当前节点添加到foreach语句中..

【讨论】:

  • 不,错过了子节点。在 OP 的代码中递归调用似乎是正确的,因为当下面没有子节点时递归停止。
  • GetAllNodes 工作正常,当我将它与其他一些 Tag 一起使用时,它按预期工作,只有在尝试处理 Root 元素时才会抛出异常。
  • @RenéVogt 同意,我认为他只需要遍历节点父母,对不起错过。
猜你喜欢
  • 2019-03-22
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 2020-07-24
  • 1970-01-01
  • 2013-01-13
  • 1970-01-01
  • 2014-12-19
相关资源
最近更新 更多