Introduction.
Adding or Deleting Nodes in TreeView Control
In this episode of the TreeView Control Tutorial, we will explore how to add or delete nodes dynamically. Before performing these actions, we will determine the position of the selected node—whether the new node should be added at the same level (sibling) or as a child node. Similarly, when deleting, we will identify the currently selected node and remove it using the Remove()
method.
This functionality makes the TreeView control more flexible, allowing users to build or modify hierarchical menus on the fly.
TreeView Control Tutorial Sessions So Far
- Microsoft TreeView Control Tutorial
- Creating an Access Menu with a TreeView Control
- Assigning Images to TreeView Control
- Assigning Images to TreeView Control-2
The Normal View of the Demo Tree View Control on MS-Access Form, with other controls.
The Data Table for TreeView Control’s Demo.
We will once again make use of the Sample table that we introduced in the very first tutorial session. This small table contains records representing the structure of common Microsoft Access objects—such as Tables, Forms, and Reports—arranged in a hierarchical order. Its simplicity makes it an excellent source for demonstrating how to add or delete nodes in the TreeView control.
The ID column (used as the TreeView Key) is defined as an AutoNumber field.
Note for New Visitors:
To get up to speed with this session, please download the Demo Database from the second tutorial page: Creating Access Menu with Tree View Control. This database contains a table named Sample and a form named frmSample. You may import both into your current project.
Additionally, the database includes another form named frmMenu. From this form, copy the Expand All and Collapse All command buttons along with their Click Event procedures into the frmSample code module to continue smoothly with this session.
As mentioned earlier, the ImageList Control—with all the images uploaded in the last tutorial—can be shared across projects. We will make a copy of that ImageList Control and bring it into this session. It will then be linked to the TreeView control on frmSample, enabling us to display node images as before.
Importing the ImageList Control with Images.
You can bring an existing ImageList Control (with uploaded images) from another database into your active project in one of the following ways:
-
Import the entire form that contains the ImageList Control into your current database. Once imported, open the form, then copy and paste the ImageList Control onto the form where you want to use it.
-
Alternatively, copy the ImageList Control directly:
-
Open the database where the ImageList Control with images already exists.
-
Copy the ImageList Control to the Clipboard.
-
If you have downloaded the Demo Database from the earlier tutorial session, you already have this control with all the uploaded images. After copying, close that database.
-
Open your target database, open the form where you want the ImageList Control, and paste it onto the form.
-
Once the ImageList Control is in place, you can link it to your TreeView control. In the CreateTreeView() subroutine, add the following code lines to pass the ImageList object reference to the TreeView control:
This ensures that the TreeView control can access and display the images stored in the ImageList.
After that, you can add the Image Key Names to the TreeView Nodes.Add() Method’s last two parameters.
These exercises we have already done in the earlier sessions. To remind you, we are using the Table and TreeView Control we created in the first Tutorial Session and implemented with the above-explained changes.
Displaying the Check-Marked Nodes Property Values.
The selected Node’s Key, Parent Key, and Text properties are displayed in the TextBoxes on the right-hand side of the TreeView control. In addition to that, a CheckBox (labeled Child Node), along with the Delete Node and Add Node command buttons, has been added to the form. Their functions will be explained shortly.
Displaying CheckBoxes on the TreeView Control
By default, the TreeView control does not display checkboxes. To enable them, you must set the appropriate property in the TreeView control’s Property Sheet.
However, it’s important to note that the TreeView’s built-in checkbox feature has limited functionality. The operations we are about to demonstrate could be performed without using checkboxes at all.
A more practical use of checkboxes in the TreeView would be for scenarios such as generating a report on selected branches of a company. In such a case, the user could tick the checkboxes next to the desired branch nodes in the Access Project Menu, and the report could then be prepared based on those selections.
Here, our aim is simply to introduce this feature and run a short demo to show how it works.
-
Open the Form containing the TreeView control in Design View.
-
Right-click on the TreeView control, highlight the TreeCtrl Object, and select Properties to open the Property Sheet.
-
In the Property Sheet, enable the CheckBox option by placing a check mark in its box.
-
Once enabled, checkboxes will be displayed next to each TreeView node, as shown in the image below.
-
The Demo TreeView Image - the first Image on this Page, Details.
Let us take a closer look at the Demo Form frmSample
shown at the top of this page.
-
The TreeView Control and the top two command buttons—Expand All and Collapse All—function exactly as we saw in the last episode.
-
On the right side, under the heading Property Values, three text boxes display the Key, ParentKey, and Text values of the currently checked node.
-
The Delete Node command button removes the selected node. If the node has child nodes, those will be removed as well.
-
To add a new node, you must first edit the Text property in the Property Values section, replacing it with the text you want to assign to the new node.
Just above the Add Node button is a Child Node check box. Together, these two controls define where the new node will be inserted:
-
If Child Node is checked, the new entry will appear as a child of the selected node.
-
If the Child Node is not checked, the new node will be added at the same level as the currently selected node.
Understanding the Add Node Action.
For example, let’s say we want to add a new node for the Hyperlink field under the Fields parent group in the data type list.
In the demo image at the top of this page, notice that the Date Field node is currently selected (checkmarked). On the right-hand Property display, its details are shown as:
-
Key: X15
-
ParentKey: X4
-
Text: Date Field
To create a new Hyperlink node:
-
Change the Text property from Date Field to Hyperlink in the Property display text box.
-
Click the Add Node button.
The result will appear as shown below:
If you place the check mark on the Fields parent node and also check the Child Node option (above the Add Node button), the new node will be added under Fields as one of its child items. The result will be the same as in the previous example.
On the other hand, if you keep the check mark on the Date Field node (instead of the parent node) and then set the Child Node option before clicking the Add Node button, the new entry will be created as a child of Date Field.
The result is as shown below:
The Child Node will be created directly under the check-marked node.
When the Add Node action is triggered, a new record must first be created in the underlying Sample table. This record will contain the new Text value (for example, HyperLink) and the corresponding ParentID value.
The AutoNumber field in the table will then generate a unique ID for the new record. This value is retrieved and assigned as the Key for the new TreeView node, ensuring its uniqueness within the control.
The Add Node sub-Routine VBA Code is given below:
Private Sub cmdAdd_Click() Dim strKey As String Dim lngKey As Long Dim strParentKey As String Dim lngParentkey As Long Dim strText As String Dim lngID As Long Dim strIDKey As String Dim childflag As Integer Dim db As DAO.Database Dim strSql As String Dim intflag As Integer Dim tmpnode As MSComctlLib.Node Dim i As Integer i = 0 For Each tmpnode In tv.Nodes If tmpnode.Checked Then tmpnode.Selected = True i = i + 1 End If Next If i > 1 Then MsgBox "Selected Nodes: " & i & vbCr & "Select only One Node to mark Addition.", vbCritical, "cmdAdd()" Exit Sub End If 'Read Property Values from Form strKey = Trim(Me![TxtKey]) lngKey = Val(Mid(strKey, 2)) strParentKey = Trim(Me![TxtParent]) lngParentkey = IIf(Len(strParentKey) > 0, Val(Mid(strParentKey, 2)), 0) strText = Trim(Me![Text]) 'Read child Node Option setting childflag = Nz(Me.ChkChild.Value, 0) intflag = 0 strSql = "INSERT INTO Sample ([Desc], [ParentID] ) " If lngParentkey = 0 And childflag = 0 Then 'Add Root-level Node, ParentKey is Blank strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & " " strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 1 ElseIf (lngParentkey >= 0) And (childflag = True) Then 'Inserts a child Node to the Check-marked Node, here Key value used as ParentKey strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & lngKey strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 2 ElseIf (lngParentkey >= 0) And (childflag = False) Then 'Inserts Node at the check-marked level, Add item under the same ParentKey strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & lngParentkey strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 3 End If Set db = CurrentDb db.Execute strSql 'Get newly created autonumber to use as Key lngID = DMax("ID", "Sample") strIDKey = KeyPrfx & CStr(lngID) On Error GoTo IdxOutofBound Select Case intflag Case 1 'Add Root-level Node, ParentKey is Blank tv.Nodes.Add , , strIDKey, strText, "folder_close", "folder_open" Case 2 'Inserts a child Node to the Check-marked Node, here Key value used as ParentKey tv.Nodes.Add strKey, tvwChild, strIDKey, strText, "left_arrow", "right_arrow" Case 3 'Inserts Node at the check-marked level, Add item under the same ParentKey tv.Nodes.Add strParentKey, tvwChild, strIDKey, strText, "left_arrow", "right_arrow" End Select tv.Refresh 'Erase Property Values from Form With Me .TxtKey = "" .TxtParent = "" .Text = "" End With Set db = Nothing cmdExpand_Click cmdAdd_Click_Exit: Exit Sub IdxOutofBound: CreateTreeView Resume cmdAdd_Click_Exit End Sub
Let us now examine the VBA code behind the Add Node process.
-
Variable Declarations
After the local variables are declared, the code scans through the TreeView Nodes to identify checkmarks and counts the number of checked items.-
If more than one Node is checked, a message is displayed, and the procedure is aborted.
-
-
Node Selection
-
Instead of check-marking, a Node can also be selected by directly clicking on it.
-
In both cases, the Checked/Selected Node is passed as a parameter to the Event procedure.
-
Using checkmarks is particularly useful when selecting multiple items—for example, choosing different sets of data for a report.
-
-
Property Extraction
-
The checked Node’s Key, ParentKey, and Text values are read from the Form controls into the variables:
-
strKey
-
strParentKey
-
strText
-
-
From these values, the numeric portions of ID and ParentID are extracted and stored in:
-
lngKey
(Node ID) -
lngParentKey
(Parent Node ID)
-
-
These will be used to build the SQL statement.
-
-
Child Node Option
-
The value of the Child Node checkbox is saved in the variable
ChildFlag
.
-
-
SQL String Preparation
-
Based on the selection and the Child Node option, three different SQL string variations are prepared:
-
Insert at the same level as the selected Node
-
Insert as a child Node under the selected Node
-
Insert as a child Node under the selected Node’s Parent
-
-
Node Creation Logic
When the Add Node button is clicked, the program determines the type of Node to be created based on the ParentID property value and the Child Node checkbox status.
-
Root-Level Node
-
Condition:
ParentID
is empty, and the Child Node option is not checked. -
Action: A Root-Level Node is created because the user had check-marked a Root Node.
-
-
Child Node
-
Condition:
ParentID >= 0
and the Child Node option is checked. -
Action: A new Node is created as a Child of the check-marked Node.
-
The Key (ID) of the check-marked Node is used as the ParentID in the new record.
-
-
-
Sibling Node (Same Level)
-
Condition:
ParentID >= 0
and Child Node option is not checked. -
Action: A new Node is created at the same level as the check-marked Node, sharing the same Parent.
-
Control Flow
-
The variable
intFlag
is assigned values 1, 2, or 3 depending on which of the above cases applies.-
1 → Root-Level Node
-
2 → Child Node
-
3 → Sibling Node
-
-
The appropriate SQL INSERT statement is executed to add a new record to the Sample table.
-
Access automatically generates a new AutoNumber ID for this record.
-
Using
DMax()
The program retrieves this new ID and assigns it as the Key value for the new Node in the TreeView control. -
Finally, based on the
intFlag
value, the Node is created in the correct position (root, child, or sibling). -
The Property display TextBox values are cleared to reset the form for the next action.
-
Deleting Node or Node with Children.
The Delete Node Option is much easier than the earlier exercise. Simply deletes the Check-Marked Node and its Children, if present, from the Tree View Control. The related records are deleted from the Table.
The VBA Code for Node Removal is given below:
Private Sub cmdDelete_Click() Dim nodId As Long, nodParent As Long Dim strSql As String Dim db As DAO.Database Dim j As Integer Dim tmpnode As MSComctlLib.Node Dim strKey As String Dim strMsg As String j = 0 ' Get check-marked Nodes count For Each tmpnode In tv.Nodes If tmpnode.Checked Then tmpnode.Selected = True strKey = tmpnode.Key j = j + 1 End If Next If j > 1 Then MsgBox "Selected Nodes: " & j & vbCr & "Select Only One Node to Delete.", vbCritical, "cmdDelete()" Exit Sub End If Set tmpnode = tv.Nodes.Item(strKey) tmpnode.Selected = True Set db = CurrentDb 'check the presense of Child Node(s) of marked Node If tmpnode.Children > 0 Then 'Warnings: ' Deleting Nodes at Random will leave orphaned Nodes ' in the Table and end up with errors, during next Tree View loading process strMsg = "The Marked Node have " & tmpnode.Children & " Children. " & vbCr & "Delete the Child Nodes also?" If MsgBox(strMsg, vbYesNo + vbCritical, "cmdDelete()") = vbYes Then 'Double check and get confirmation. strMsg = "Delete Only the deepest set of Child Nodes" & vbCr strMsg = strMsg & "and their Parent Node at one time." & vbCr & vbCr strMsg = strMsg & "Are you sure to Proceed..?" If MsgBox(strMsg, vbYesNo + vbCritical, "cmdDelete()") = vbYes Then Do Until tmpnode.Children = 0 nodId = Val(Mid(tmpnode.Child.Key, 2)) 'Delete Child Node tv.Nodes.Remove tmpnode.Child.Index 'Delete the related record strSql = "DELETE Sample.*, Sample.ID FROM Sample WHERE (((Sample.ID)= " & nodId & "));" db.Execute strSql Loop Else Exit Sub End If Else Exit Sub End If End If nodId = Val(Mid(tmpnode.Key, 2)) 'Delete Parent tv.Nodes.Remove tmpnode.Key tv.Refresh 'Delete Marked Record strSql = "DELETE Sample.*, Sample.ID FROM Sample WHERE (((Sample.ID)= " & nodId & "));" db.Execute strSql 'Erase Property Values from Form With Me .TxtKey = "" .TxtParent = "" .Text = "" End With Set db = Nothing End Sub
Delete Node Logic
After the local variable declarations, the procedure to delete a Node follows these steps:
-
Count Check-Marked Nodes
-
A
For Each … Next
loop scans through the Nodes. -
If more than one Node is check-marked, a message is displayed and the program is aborted.
-
If exactly one Node is check-marked, the program continues.
-
-
Validation Check – Child Node Presence
-
The program checks if the selected Node has Child Node(s).
-
If Child Nodes are found:
-
A message is displayed to inform the user.
-
The user must reconfirm their intention before proceeding.
-
Deletion must occur from the deepest level first (child → parent → grandparent).
-
-
⚠️ Important Rule for Simplicity
-
Always delete the deepest-level Child Nodes first, or delete a parent with its immediate children.
-
Avoid deleting higher-level (grandparent) Nodes directly, as this can leave some Nodes orphaned.
-
Orphaned Nodes will cause errors the next time the TreeView is opened.
-
Delete Operation
-
If Child Nodes exist:
-
Each Child Node is removed one by one, along with its corresponding record in the underlying table.
-
Finally, the selected Parent Node is deleted, both from the TreeView and the table.
-
-
If no Child Nodes are present:
-
The selected Node is deleted immediately, and its corresponding table record is also deleted.
-
-
-
Cleanup
-
After deletion, the Property display TextBox values (Key, ParentKey, and Text) are cleared to reset the form.
-
The Form frmSample’s Complete Class Module VBA Code.
Complete VBA Code in frmSample’s Class Module
Below is the complete code for the form frmSample, which includes:
-
Utility subroutines for Expanding and Collapsing Nodes
-
The TreeView0_NodeCheck() event procedure
-
The cmdExit_Click() event
-
The Form_Load() procedure
-
The CreateTreeView() subroutine
-
And the full implementation of the Add Node and Delete Node logic
Option Compare Database Option Explicit Dim tv As MSComctlLib.TreeView Dim ImgList As MSComctlLib.ImageList Const KeyPrfx As String = "X" Private Sub cmdAdd_Click() Dim strKey As String Dim lngKey As Long Dim strParentKey As String Dim lngParentkey As Long Dim strText As String Dim lngID As Long Dim strIDKey As String Dim childflag As Integer Dim db As DAO.Database Dim strSql As String Dim intflag As Integer Dim tmpnode As MSComctlLib.Node Dim i As Integer i = 0 For Each tmpnode In tv.Nodes If tmpnode.Checked Then tmpnode.Selected = True i = i + 1 End If Next If i > 1 Then MsgBox "Selected Nodes: " & i & vbCr & "Select only One Node to mark Addition.", vbCritical, "cmdAdd()" Exit Sub End If 'Read Property Values from Form strKey = Trim(Me![TxtKey]) lngKey = Val(Mid(strKey, 2)) strParentKey = Trim(Me![TxtParent]) lngParentkey = IIf(Len(strParentKey) > 0, Val(Mid(strParentKey, 2)), 0) strText = Trim(Me![Text]) 'Read child Node Option setting childflag = Nz(Me.ChkChild.Value, 0) intflag = 0 strSql = "INSERT INTO Sample ([Desc], [ParentID] ) " If lngParentkey = 0 And childflag = 0 Then 'Add Root-level Node, ParentKey is Blank strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & " " strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 1 ElseIf (lngParentkey >= 0) And (childflag = True) Then 'Inserts a child Node to the Check-marked Node, here Key value used as ParentKey strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & lngKey strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 2 ElseIf (lngParentkey >= 0) And (childflag = False) Then 'Inserts Node at the check-marked level, Add item under the same ParentKey strSql = strSql & "SELECT '" & strText & "' AS [Desc], '" & lngParentkey strSql = strSql & "' AS ParentID FROM Sample WHERE ((Sample.ID = 1));" intflag = 3 End If Set db = CurrentDb db.Execute strSql 'Get newly created autonumber to use as Key lngID = DMax("ID", "Sample") strIDKey = KeyPrfx & CStr(lngID) On Error GoTo IdxOutofBound Select Case intflag Case 1 'Add Root-level Node, ParentKey is Blank tv.Nodes.Add , , strIDKey, strText, "folder_close", "folder_open" Case 2 'Inserts a child Node to the Check-marked Node, here Key value used as ParentKey tv.Nodes.Add strKey, tvwChild, strIDKey, strText, "left_arrow", "right_arrow" Case 3 'Inserts Node at the check-marked level, Add item under the same ParentKey tv.Nodes.Add strParentKey, tvwChild, strIDKey, strText, "left_arrow", "right_arrow" End Select tv.Refresh 'Erase Property Values from Form With Me .TxtKey = "" .TxtParent = "" .Text = "" End With Set db = Nothing cmdExpand_Click cmdAdd_Click_Exit: Exit Sub IdxOutofBound: CreateTreeView Resume cmdAdd_Click_Exit End Sub Private Sub cmdClose_Click() DoCmd.Close End Sub Private Sub cmdDelete_Click() Dim nodId As Long, nodParent As Long Dim strSql As String Dim db As DAO.Database Dim j As Integer Dim tmpnode As MSComctlLib.Node Dim strKey As String Dim strMsg As String j = 0 ' Get check-marked Nodes count For Each tmpnode In tv.Nodes If tmpnode.Checked Then tmpnode.Selected = True strKey = tmpnode.Key j = j + 1 End If Next If j > 1 Then MsgBox "Selected Nodes: " & j & vbCr & "Select Only One Node to Delete.", vbCritical, "cmdDelete()" Exit Sub End If Set tmpnode = tv.Nodes.Item(strKey) tmpnode.Selected = True Set db = CurrentDb 'check the presense of Child Node(s) of marked Node If tmpnode.Children > 0 Then 'Warnings: ' Deleting Nodes at Random will leave orphaned Nodes ' in the Table and end up with errors, during next Tree View loading process strMsg = "The Marked Node have " & tmpnode.Children & " Children. " & vbCr & "Delete the Child Nodes also?" If MsgBox(strMsg, vbYesNo + vbCritical, "cmdDelete()") = vbYes Then 'Double check and get confirmation. strMsg = "Delete Only the deepest set of Child Nodes" & vbCr strMsg = strMsg & "and their Parent Node at one time." & vbCr & vbCr strMsg = strMsg & "Are you sure to Proceed..?" If MsgBox(strMsg, vbYesNo + vbCritical, "cmdDelete()") = vbYes Then Do Until tmpnode.Children = 0 nodId = Val(Mid(tmpnode.Child.Key, 2)) 'Delete Child Node tv.Nodes.Remove tmpnode.Child.Index 'Delete the related record strSql = "DELETE Sample.*, Sample.ID FROM Sample WHERE (((Sample.ID)= " & nodId & "));" db.Execute strSql Loop Else Exit Sub End If Else Exit Sub End If End If nodId = Val(Mid(tmpnode.Key, 2)) 'Delete Parent tv.Nodes.Remove tmpnode.Key tv.Refresh 'Delete Marked Record strSql = "DELETE Sample.*, Sample.ID FROM Sample WHERE (((Sample.ID)= " & nodId & "));" db.Execute strSql 'Erase Property Values from Form With Me .TxtKey = "" .TxtParent = "" .Text = "" End With Set db = Nothing End Sub Private Sub cmdExpand_Click() Dim nodExp As MSComctlLib.Node For Each nodExp In tv.Nodes nodExp.Expanded = True Next End Sub Private Sub cmdCollapse_Click() Dim nodExp As MSComctlLib.Node For Each nodExp In tv.Nodes nodExp.Expanded = False Next End Sub Private Sub Form_Load() CreateTreeView cmdExpand_Click End Sub Private Sub CreateTreeView() Dim db As Database Dim rst As Recordset Dim nodKey As String Dim ParentKey As String Dim strText As String Dim strSql As String Set tv = Me.TreeView0.Object tv.Nodes.Clear 'Pass ImageList control reference to TreeView's ImageList Property. Set ImgList = Me.ImageList0.Object tv.ImageList = ImgList strSql = "SELECT ID, Desc, ParentID FROM Sample;" Set db = CurrentDb Set rst = db.OpenRecordset("sample", dbOpenTable) Do While Not rst.EOF And Not rst.BOF If Nz(rst!ParentID, "") = "" Then nodKey = KeyPrfx & CStr(rst!ID) strText = rst!desc tv.Nodes.Add , , nodKey, strText, "folder_close", "folder_open" Else ParentKey = KeyPrfx & CStr(rst!ParentID) nodKey = KeyPrfx & CStr(rst!ID) strText = rst!desc tv.Nodes.Add ParentKey, tvwChild, nodKey, strText, "left_arrow", "right_arrow" End If rst.MoveNext Loop rst.Close On Error GoTo 0 Set rst = Nothing Set db = Nothing End Sub Private Sub TreeView0_NodeCheck(ByVal Node As Object) Dim xnode As MSComctlLib.Node Set xnode = Node If xnode.Checked Then xnode.Selected = True With Me .TxtKey = xnode.Key If xnode.Text = xnode.FullPath Then .TxtParent = "" Else .TxtParent = xnode.Parent.Key End If .Text = xnode.Text End With Else xnode.Selected = False With Me .TxtKey = "" .TxtParent = "" .Text = "" End With End If End Sub
📌 Notes for Readers:
-
The Add Node and Delete Node button procedures should contain the full logic we explained in the last two sections (SQL insert for new records, DMax() to fetch ID, and deletion with child validation).
-
In this complete listing, I’ve left placeholders (
' (Full Add Node logic goes here)
) so you can easily copy and paste the previously explained code blocks in.
The Design View of the frmSample Form is given below:
Your Observations, comments, and suggestions are welcome.
The Demo Database is attached for Download.