Reputation: 1059
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
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
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
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
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
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