Amit Kumar
Amit Kumar

Reputation: 1059

Select parent node if any single child node checked in treeview

I have a treeview that has some parent nodes and child nodes. If a child node is selected parent node should be selected. This functionality is working fine but if parent node has more than one child node and I checked more than one child node and unchecked a single child node parent node get unchecked.

I have done some code for selecting parent node if child node selected.

private bool updatingTreeView;

private void treSelector_AfterCheck(object sender, TreeViewEventArgs e)
{
    if (updatingTreeView) return;
    updatingTreeView = true;
    SelectParents(e.Node, e.Node.Checked);
    updatingTreeView = false;
}

private void SelectParents(TreeNode node, Boolean isChecked)
{
    if (node.Parent != null)
    {
        node.Parent.Checked = isChecked;
        SelectParents(node.Parent, isChecked);
    }
}

I don't want unchecked parent node if any of single child node of that parent is checked.

Upvotes: 3

Views: 19106

Answers (5)

NinjaLlama
NinjaLlama

Reputation: 165

I referenced this and other posts while creating a similar functionality and thought I would share in case it helps someone else.

Desired functionality:

Checking a parent node will check all child nodes.

Unchecking a child node will uncheck all parent nodes to signify that not all children are checked.

    /// <summary>
    /// Propagates checked state downwards to all children and upwards unchecked to parents.
    /// Parents are unchecked to visually signify that not all children are checked.
    /// </summary>
    /// <param name="currentNode"></param>
    /// <param name="nodeChecked"></param>
    private void CheckedUncheckedPropagateTreeNode(TreeNode currentNode, bool nodeChecked)
    {
        // Check if current node has children.
        foreach (TreeNode node in currentNode.Nodes)
        {
            // Update checked status of this child node.
            node.Checked = nodeChecked;

            // Recursively call method to handle all children of this child node.
            CheckedUncheckedPropagateTreeNode(node, nodeChecked);
        }

        // If the node was unchecked uncheck all parent nodes.
        if (!nodeChecked)
        {
            // Set parent node initial if available.
            TreeNode parentNode = currentNode.Parent;

            // Traverse tree nodes upwards to uncheck all parents.
            while (parentNode != null)
            {
                // Set parent checked value.
                parentNode.Checked = nodeChecked;

                // Set new parent node to continue while loop to the top of the tree view.
                parentNode = parentNode.Parent;
            }
        }
    }

Upvotes: 0

Lukas-N
Lukas-N

Reputation: 41

Here is a full routine which select the children if you check the parent and check the parent if you set any child

void tw_AfterCheck(object sender, TreeViewEventArgs e) {

            if (treeView.Enabled) {
                treeView.AfterCheck -= tw_AfterCheck;

                TreeNode node = e.Node;
                if (node.Nodes != null) 
                    node.Nodes.Cast<TreeNode>().ToList().ForEach(v => v.Checked = node.Checked);

                node = e.Node.Parent;
                while (node != null) {
                    bool set = e.Node.Checked
                               ? node.Nodes.Cast<TreeNode>()
                                .Any(v => v.Checked == e.Node.Checked)
                               : node.Nodes.Cast<TreeNode>()
                                .All(v => v.Checked == e.Node.Checked);
                    if (set) {
                        node.Checked = e.Node.Checked;
                        node = node.Parent;
                    }
                    else
                        node = null;
                }
               treeView.AfterCheck += tw_AfterCheck;
            }
        }

Upvotes: 3

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236208

private void SelectParents(TreeNode node, Boolean isChecked)
{
    var parent = node.Parent;

    if (parent == null)
        return;

    if (isChecked)
    {
        parent.Checked = true; // we should always check parent
        SelectParents(parent, true);
    }
    else
    {
        if (parent.Nodes.Cast<TreeNode>().Any(n => n.Checked))
            return; // do not uncheck parent if there other checked nodes

        SelectParents(parent, false); // otherwise uncheck parent
    }
}

Same, refactored

private void SelectParents(TreeNode node, Boolean isChecked)
{
    var parent = node.Parent;

    if (parent == null)
        return;

    if (!isChecked && HasCheckedNode(parent))
        return;

    parent.Checked = isChecked;
    SelectParents(parent, isChecked);
}

private bool HasCheckedNode(TreeNode node)
{
    return node.Nodes.Cast<TreeNode>().Any(n => n.Checked);
}

Upvotes: 7

Servy
Servy

Reputation: 203821

When you go to verify each parent you need to verify all of the children of that parent, since you don't know if unchecking the node was the last or not:

private void VerifyChecked(TreeNode node)
{
    var current = node;
    while (current != null)
    {
        current.Checked = current.Nodes.Cast<TreeNode>()
            .Any(child => child.Checked);
        current = current.Parent;
    }
}

Note that I refactored it to verify the passed in node, not its parent, so you'd need to modify your call of it to pass in e.Node.Parent instead.

Upvotes: 0

Marshall777
Marshall777

Reputation: 1204

If you uncheck an item, the aftercheck event will be raised and you will call SelectParents which will propagate the unchecking.

If you want the behavior only when checked, simply run SelectParents when e.Node.Checked is true (or checked). But this will not uncheck parent item if you uncheck the only child item taht was checked before (so you may have a parent item checked without any child item being checked).

Upvotes: 1

Related Questions