Как эффективно удалить отмеченные элементы из TreeView?

Как легко перебрать все узлы в TreeView, проверить их свойство .Checked и затем удалить все проверенные узлы?

Это кажется простым, но вы не должны изменять коллекцию, через которую вы выполняете итерацию, исключая возможность цикла foreach. (Вызов .Nodes.Remove изменяет коллекцию.) При попытке сделать это эффект заключается в том, что удаляется только около половины узлов .Checked.

Даже если бы кто-то использовал два прохода: сначала создание списка временных индексов, а затем удаление по индексу на втором проходе - индексы изменялись бы при каждом удалении, нарушая целостность списка индексов.

Итак, каков наиболее эффективный способ сделать это?

Вот пример кода, который выглядит хорошо, но на самом деле удаляет только около половины узлов .Checked:

            foreach (TreeNode parent in treeView.Nodes)
            {
                if (parent.Checked)
                {
                    treeView.Nodes.Remove(parent);
                }
                else
                {
                    foreach (TreeNode child in parent.Nodes)
                    {
                        if (child.Checked) parent.Nodes.Remove(child);
                    }
                }
            }

(Да, намерение состоит только в том, чтобы обрезать узлы из двухуровневого дерева.)


person James    schedule 05.05.2009    source источник


Ответы (4)


Это удалит узлы после их перечисления и может использоваться рекурсивно для n-уровневых узлов.

void RemoveCheckedNodes(TreeNodeCollection nodes)
{
    List<TreeNode> checkedNodes = new List<TreeNode>();

    foreach (TreeNode node in nodes)
    {
        if (node.Checked)
        {
            checkedNodes.Add(node);
        }
        else
        {
            RemoveCheckedNodes(nodes.ChildNodes);
        }
    }

    foreach (TreeNode checkedNode in checkedNodes)
    {
        nodes.Remove(checkedNode);
    }
}
person Jon B    schedule 05.05.2009

Попробуйте пройтись по узлам в обратном направлении. Таким образом, ваш индекс не превышает размер вашего узла:

for( int ndx = nodes.Count; ndx > 0; ndx--)
{
  TreeNode node = nodes[ndx-1];
  if (node.Checked)
  {
     nodes.Remove(node);
  }
   // Recurse through the child nodes...
}
person Jay Mooney    schedule 05.05.2009
comment
Это самый эффективный метод. - person Romias; 24.12.2009
comment
Старый вопрос, но +1 за то, что это самый эффективный метод. - person TimFoolery; 28.06.2013
comment
Возвращаясь к этому... несколько модификаций сделали бы его немного быстрее... внесение следующих изменений в заголовок цикла for: int ndx = nodes.Count-1 и ndx >= 0 позволит вам избежать -1, возникающего при каждом проходе цикла. По большому счету, несколько дополнительных вычитаний ничего не значат, но почему бы и нет? - person TimFoolery; 07.07.2013

Если вы хотите сделать это эффективно, вам нужно отслеживать проверенные узлы по мере их проверки. Сохраните проверенные узлы дерева в списке (и удалите их, поскольку они не отмечены).

Если у вас есть уникальный ключ и МНОГО узлов, которые нужно отслеживать, вы также можете подумать о словаре. Но если вы имеете дело только с 10-50, это, вероятно, не будет иметь большого значения.

Затем вместо того, чтобы перебирать все дерево, вы просто перебираете свой (меньший) список узлов.

person Chris Brandsma    schedule 05.05.2009

Во время итерации вы можете создать новый список непроверенных элементов, а затем повторно привязать свое древовидное представление к этому новому списку (отбросив старый).

person Steven Evers    schedule 05.05.2009