Новичок: TreeView с флажками и рекурсией в Winforms

первый пост и новый кодер, так что будьте со мной, если вам нужно больше информации, чем я даю. Я пытаюсь создать древовидную структуру с флажками в иерархии (см. рис.). Моя проблема в том, что я хочу создать какую-то рекурсию, которая отменяет выбор и выбирает дочерние узлы, когда проверяются родительские узлы, или наоборот.

Я использую VS с winforms и уже 2 дня гуглю, как это сделать, к сожалению, онлайн-примеры либо слишком сложны для меня, либо не работают. Я нашел Учебник о том, как сделать это именно с неопределенными флажками, что было бы большим бонусом, но это для 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)
    {
        
    }

Дерево изображений с (не)проверкой всех кнопок

Не уверен, в чем ваша проблема. Единственное, что я вижу неправильно, это когда rootNode.Nodes имеет значение null. Поэтому вам нужен оператор IF для возврата из CheckAllNodes/UnCheckAllNodes, когда rootNode.Nodes имеет значение null.

jdweng 23.12.2020 11:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
421
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я согласен с @jdweng, вы используете рекурсию в checkChildren(). Базовый случай отсутствует. В рекурсии checkChildren добавьте базовый регистр перед
foreach (TreeNode node in rootNode.Nodes)
if node is Null : rootNode=isChecked

Ответ принят как подходящий

Давайте создадим пару методов расширения для типа 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);
}

Вскоре вы заметите, что иногда это решение не работает должным образом, когда вы быстро щелкаете по флажкам, поскольку по умолчанию они не получают сообщения о двойном щелчке мыши. Затем подпишитесь на этот пост или на этот, чтобы решить эту проблему. Пока нажмите медленно.

Если вы предпочитаете использовать кнопки для переключения состояния проверки, удалите обработчик AfterCheck и вместо этого выполните:

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

private void btnUncheckAll_Click(object sender, EventArgs e)
{
    ToggleCheck(treeView1.SelectedNode, 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;
}

Эй, большое спасибо за это. Почти делает именно то, что я хочу. Извиняюсь за поздний ответ, так как был в отпуске. Как мне сделать так, чтобы он проверял/снимал галочку на лету? Вместо того, чтобы делать это кнопкой на выбранных узлах?

Breakwin 02.01.2021 17:38

@Breakwin С возвращением. Если вы проверите первую версию моего ответа, вы увидите эту часть. Я думал, что вам это не нужно, поэтому я удалил его. Я добавлю это снова.

dr.null 03.01.2021 01:22

блестяще, это получилось. большое спасибо. Мне просто пришлось добавить событие aftercheck дополнительно. По-видимому, это не было добавлено в древовидную структуру, когда я ее создал. learn.microsoft.com/en-us/dotnet/api/…

Breakwin 04.01.2021 14:17

Другие вопросы по теме