Advanced User Interface Controls and Components
Created: Mar 05, 2009
Updated: Jun 01, 2013
By default the IntegralUI TreeView displays two states of the checkboxes in each node. By setting the CheckMode property to ThreeState, every check box can display one of these three states: Unchecked, Indeterminate and Checked.
However, the check box value is not automatically updated for any child or parent node. To do that, the AfterCheck event needs to be handled. Here is how:
At first you need to have a TreeView with some root nodes and child nodes in it. Also nodes must have check box visible and the tri-state mode enabled. In the following code we will create a small tree hierarchy:
private void InitList()
{
// Suspend the treeview layout to increase performance
this.treeView1.SuspendUpdate();
this.treeView1.Nodes.Clear();
// Make sure the check boxes are displayed, and they accepts three values
this.treeView1.CheckBoxes = true;
this.treeView1.CheckMode = LidorSystems.IntegralUI.CheckMode.ThreeState;
LidorSystems.IntegralUI.Lists.TreeNode node = null;
for (int i = 0; i < 10; i++)
{
// Create a new node
node = new LidorSystems.IntegralUI.Lists.TreeNode("Node " + i.ToString());
// Create a child nodes for this node
CreateChildNodes(node, 0);
// Add the node as the root node
this.treeView1.Nodes.Add(node);
}
// Resume the treeview layout and update the control
this.treeView1.ResumeUpdate();
}
private void CreateChildNodes(LidorSystems.IntegralUI.Lists.TreeNode parentNode, int level)
{
//In this example only 2 levels in hierarchy are allowed
if (level == 2)
return;
else
{
LidorSystems.IntegralUI.Lists.TreeNode node = null;
for (int i = 0; i < 3; i++)
{
// Create a new child node
node = new LidorSystems.IntegralUI.Lists.TreeNode("Node " + level.ToString() + i.ToString());
// Create a child nodes for this node
CreateChildNodes(node, level + 1);
// Add the node to the parent node
parentNode.Nodes.Add(node);
}
}
}
Private Sub InitList()
' Suspend the treeview layout to increase performance
Me.treeView1.SuspendUpdate()
Me.treeView1.Nodes.Clear()
' Make sure the check boxes are displayed, and they accepts three values
Me.treeView1.CheckBoxes = True
Me.treeView1.CheckMode = LidorSystems.IntegralUI.CheckMode.ThreeState
Dim node As LidorSystems.IntegralUI.Lists.TreeNode = Nothing
For i As Integer = 0 To 9
' Create a new node
node = New LidorSystems.IntegralUI.Lists.TreeNode("Node " & i.ToString())
' Create a child nodes for this node
CreateChildNodes(node, 0)
' Add the node as the root node
Me.treeView1.Nodes.Add(node)
Next
' Resume the treeview layout and update the control
Me.treeView1.ResumeUpdate()
End Sub
Private Sub CreateChildNodes(ByVal parentNode As LidorSystems.IntegralUI.Lists.TreeNode, ByVal level As Integer)
'In this example only 2 levels in hierarchy are allowed
If level = 2 Then
Exit Sub
Else
Dim node As LidorSystems.IntegralUI.Lists.TreeNode = Nothing
For i As Integer = 0 To 2
' Create a new child node
node = New LidorSystems.IntegralUI.Lists.TreeNode(("Node " & level.ToString()) + i.ToString())
' Create a child nodes for this node
CreateChildNodes(node, level + 1)
' Add the node to the parent node
parentNode.Nodes.Add(node)
Next
End If
End Sub
Then by handling the AfterCheck event, we will cycle through child and parent nodes and change the value of their check box. The UpdateChild method is used to change the check box value of the child nodes, while UpdateParent method is used to change the check box value of the parent nodes.
In order to avoid callback internally whenever a value of check box s changed when AfterCheck event is called, we will use a variable which determines whether the event is already called or not.
private bool suspendCallBack = false;
. . .
private void treeView1_AfterCheck(object sender, LidorSystems.IntegralUI.ObjectEventArgs e)
{
if (!suspendCallBack && e.Object is LidorSystems.IntegralUI.Lists.TreeNode)
{
LidorSystems.IntegralUI.Lists.TreeNode node = (LidorSystems.IntegralUI.Lists.TreeNode)e.Object;
// Suspend the further calling of BeforeCheck and AfterCheck events
suspendCallBack = true;
// Use this method to update the state for all child nodes of this node
UpdateChild(node);
// Use this method to update the state for all parent nodes of this node
UpdateParent(node);
// Resume the calling of BeforeCheck and AfterCheck events
suspendCallBack = false;
}
}
private void UpdateChild(LidorSystems.IntegralUI.Lists.TreeNode node)
{
// If the parent node is not Indeterminate
// change the state of all child nodes
if (node.CheckState != CheckState.Indeterminate)
{
CheckState state = node.CheckState;
foreach (LidorSystems.IntegralUI.Lists.TreeNode childNode in node.Nodes)
{
if (childNode.Visible)
{
// Change the CheckState of the childNode
childNode.CheckState = state;
// Repeat the whole cycle for other child nodes
UpdateChild(childNode);
}
}
}
}
private void UpdateParent(LidorSystems.IntegralUI.Lists.TreeNode node)
{
// Hold te number of unchecked and visible nodes
int numUnchecked = 0;
int numVisible = 0;
// Let presume that by default that node is checked
CheckState state = CheckState.Checked;
// Get the parent node
LidorSystems.IntegralUI.Lists.TreeNode parentNode = node.Parent;
while (parentNode != null)
{
numUnchecked = 0;
numVisible = 0;
state = CheckState.Checked;
foreach (LidorSystems.IntegralUI.Lists.TreeNode childNode in parentNode.Nodes)
{
if (childNode.Visible)
{
numVisible++;
// If there is at least one Indeterminate node, exit the loop.
// The state for the parent node will be also Indeterminate
if (childNode.CheckState == CheckState.Indeterminate)
{
state = CheckState.Indeterminate;
break;
}
// Count the unchecked nodes
else if (childNode.CheckState == CheckState.Unchecked)
numUnchecked++;
}
}
if (numVisible > 0)
{
// If there are no unchecked nodes and there is at least one indeterminate node,
// that means that all child nodes are checked. So, the parent node will also be Checked
if (numUnchecked == 0 && state != CheckState.Indeterminate)
state = CheckState.Checked;
// If number of visible and unchecked nodes are equal, then parent node will be Unchecked
else if (numUnchecked == numVisible)
state = CheckState.Unchecked;
// In any other case, then parent node will be Indeterminate
else
state = CheckState.Indeterminate;
}
// If there are no visible nodes, the parent node will be unchecked
else
state = CheckState.Unchecked;
// Change the CheckState of the parent node,
parentNode.CheckState = state;
// Repeat the whole cycle for other parent nodes
parentNode = parentNode.Parent;
}
}
Private suspendCallBack as Boolean = False
. . .
Private Sub treeView1_AfterCheck(ByVal sender As Object, ByVal e As LidorSystems.IntegralUI.ObjectEventArgs)
If Not suspendCallBack AndAlso TypeOf e.Object Is LidorSystems.IntegralUI.Lists.TreeNode Then
Dim node As LidorSystems.IntegralUI.Lists.TreeNode = DirectCast(e.Object, LidorSystems.IntegralUI.Lists.TreeNode)
' Suspend the further calling of BeforeCheck and AfterCheck events
suspendCallBack = True
' Use this method to update the state for all child nodes of this node
UpdateChild(node)
' Use this method to update the state for all parent nodes of this node
UpdateParent(node)
' Resume the calling of BeforeCheck and AfterCheck events
suspendCallBack = False
End If
End Sub
Private Sub UpdateChild(ByVal node As LidorSystems.IntegralUI.Lists.TreeNode)
' If the parent node is not Indeterminate
' change the state of all child nodes
If node.CheckState <> CheckState.Indeterminate Then
Dim state As CheckState = node.CheckState
For Each childNode As LidorSystems.IntegralUI.Lists.TreeNode In node.Nodes
If childNode.Visible Then
' Use this method to change the CheckState of the childNode,
' without triggerring the BeforeCheck and AfterCheck events
' this.treeView1.ChangeCheckState(childNode, state);
childNode.CheckState = state
' Repeat the whole cycle for other child nodes
UpdateChild(childNode)
End If
Next
End If
End Sub
Private Sub UpdateParent(ByVal node As LidorSystems.IntegralUI.Lists.TreeNode)
' Hold te number of unchecked and visible nodes
Dim numUnchecked As Integer = 0
Dim numVisible As Integer = 0
' Let presume that by default that node is checked
Dim state As CheckState = CheckState.Checked
' Get the parent node
Dim parentNode As LidorSystems.IntegralUI.Lists.TreeNode = node.Parent
While parentNode IsNot Nothing
numUnchecked = 0
numVisible = 0
state = CheckState.Checked
For Each childNode As LidorSystems.IntegralUI.Lists.TreeNode In parentNode.Nodes
If childNode.Visible Then
numVisible += 1
' If there is at least one Indeterminate node, exit the loop.
' The state for the parent node will be also Indeterminate
If childNode.CheckState = CheckState.Indeterminate Then
state = CheckState.Indeterminate
Exit For
' Count the unchecked nodes
ElseIf childNode.CheckState = CheckState.Unchecked Then
numUnchecked += 1
End If
End If
Next
If numVisible > 0 Then
' If there are no unchecked nodes and there is at least one indeterminate node,
' that means that all child nodes are checked. So, the parent node will also be Checked
If numUnchecked = 0 AndAlso state <> CheckState.Indeterminate Then
state = CheckState.Checked
' If number of visible and unchecked nodes are equal, then parent node will be Unchecked
ElseIf numUnchecked = numVisible Then
state = CheckState.Unchecked
Else
' In any other case, then parent node will be Indeterminate
state = CheckState.Indeterminate
End If
Else
' If there are no visible nodes, the parent node will be unchecked
state = CheckState.Unchecked
End If
' Use this method to change the CheckState of the node,
' without triggerring the BeforeCheck and AfterCheck events
' this.treeView1.ChangeCheckState(parentNode, state);
parentNode.CheckState = state
' Repeat the whole cycle for other parent nodes
parentNode = parentNode.Parent
End While
End Sub
A complete sample project in C# .NET and VB .NET, showing how to create nodes with tri-state checkboxes in TreeView, is available for download from here: TreeView .NET - Three State Checkboxes