I want to use MS Forms tree view for one of my folder and used span folder option to populate the tblFileFolder using demo shared by @MajP at Hierarchical Data, Recursion, Tree-Views, and a Custom Class to Assis. No of records in the table are 3000. I am then using TreeViewRecordLoader Class to generate my MS Forms tree view which add nodes from the table recursively due to which it takes about a minute or so and during which it seems that access got stuck. Is it possible to load the tree view faster instead of adding one by one node or is there any other way to speed up the tree view generation?
I posted this question in the original thread by @MajP and was suggested to start new thread (which I should have done in the first place).
The file structure is now already stored in the table and is not being done again. That was a one time process for my case. Now the time is taken in loading the tree. Light load option was suggested which seems better option but it was for TreeView control and unfortunately not available for MS Forms Tree view.
I was playing with the tree view control and found I can just populate all nodes of a tree view control and then assign a parent to each of them with another pass... You guys see any problem in that method? I know there's a bunch of threads about this topic by @MajP, but this is likely a...
www.access-programmers.co.uk
It just adds all nodes as orphans and then it gives them parents. It's not recursion, but it'd be good to know if it loads faster.
So everyone knows what we are talking about there are two Treeviews that I know of that work in Access.
The most common is the ActiveX Treeview located in the MS CommonControls 6.0
The other was made by JKP and can be found here towards the bottom.
The JKP version is home made using MS Forms controls native to VBA. It is an extremely impressive amount of vba.
The advantage of the native MS Forms version is that it overcomes a lot of portability issues of using Active X.
Most of the methods and properties mirror the Active X control so it is relatively easy to build/convert a JKP treeview without learning the class methods and properties.
Let me explain the logic for the light load that I coded into my class that uses the ActiveX control.
Instead of fully populating the Tree with all the nodes, I only populate the top level visible nodes. For each visible node I also only load one child node and for each child node a single child node (recursivel)
In my demo in the below thread, I load a Treeview with 10K records and it loads instantaneously:
I have had some people ask questions on working with Hierarchical data and Tree Views, which often involves using recursion. If not familiar with recursion this is where a procedure repeatedly calls itself until hitting a condition where it is told to stop. This is used often where you have to...
www.access-programmers.co.uk
When the tree loads the following nodes are loaded and only one child (no sub nodes).
The reason for the child node is that is how you get the Plus Sign. If there was only the 9 visible nodes, none of these nodes would appear expandable.
Assume you Click on Linda Powlowski. Additional nodes get loaded. The rest of linda's "child" nodes, and again one child node for each loaded child.
So originally Mallie was already loaded. It then loads Charlotte and the rest of Linda's child nodes.
So yes this same functionality could be added to the MSForms Class I built. Did this take me a long time to figure out? Yes. But since the two "controls" mirror each other I should be able to figure it out a lot faster. So when I get time I will see if I can add it. I always planned to write a complete helper class for this, but never got around.
Here is some of the light load logic. It only differs from the full load in that there is no loop to recurse all children. It recurses a single child only.
Code:
Public Sub addLiteBranch(parentIDValue As Variant, identifier)
On Error GoTo errLable
Dim strCriteria As String
Dim bk As String
Dim currentID As Variant
Dim currentText As String
Dim currentNode As Node
Dim currentImage As String
Dim ParentNode As Node
Dim strKey As String
Dim rsTree As DAO.Recordset
Set rsTree = Me.Recordset
strCriteria = Parent_ID_Field & " = '" & parentIDValue & "'"
'Since it is not a root node must determine the parent node. All node keys are "ID " & the primary key
Set ParentNode = Me.Nodes(parentIDValue)
rsTree.FindFirst (strCriteria)
'Only going to get the top node
If Not rsTree.NoMatch Then
currentID = rsTree.Fields(ID_Field)
currentText = rsTree.Fields(Node_Text_Field)
identifier = rsTree.Fields(Identifier_Field)
Set currentNode = Me.Nodes.Add(ParentNode, tvwChild, currentID, currentText)
currentNode.Tag = identifier
If mLoadImages Then
currentImage = Nz(rsTree.Fields(Image_Name_Field), "")
If currentImage <> "" Then currentNode.Image = currentImage
End If
bk = rsTree.Bookmark
Call addLiteBranch(currentID, identifier)
rsTree.Bookmark = bk
rsTree.FindNext (strCriteria)
End If
Exit Sub
errLable:
MsgBox Err.Number & " " & Err.Description & " In addBranch"
If MsgBox("Do you want to exit the loop?", vbYesNo, "Error In Loop") = vbYes Then
Exit Sub
Else
Resume Next
End If
End Sub
More importanly when you click on a node it does the same
Code:
Private Sub mTVW_Expand(ByVal Node As MSComctlLib.Node)
'Flush out any remaining nodes in this expanded level and liteload child level
'This procedure is associated with a light load. Occurs when you expand the branch
On Error GoTo errlbl
Dim strCriteria As String
Dim bk As String
Dim currentID As Variant
Dim currentText As String
Dim currentNode As Node
Dim ParentNode As Node
Dim currentIdentifier As String
Dim currentImage As String
Dim rsTree As DAO.Recordset
'Debug.Print Node.Text & " " & "Selected Node"
If Node.Children > 1 Then Exit Sub ' node has already been expanded
Set rsTree = Me.Recordset
strCriteria = Parent_ID_Field & " = '" & Node.key & "'"
rsTree.FindFirst (strCriteria)
Debug.Print strCriteria
If rsTree.NoMatch Then
' MsgBox "There is no record with a Parent ID of " & Node.key
End If
Do Until rsTree.NoMatch
currentID = rsTree.Fields(ID_Field)
Debug.Print currentID & " current"
currentText = rsTree.Fields(Node_Text_Field)
currentIdentifier = rsTree.Fields(Identifier_Field)
If Node.Child.key = currentID Then
Node.Child.Tag = currentIdentifier
RaiseEvent ExpandedBranch(Node.Child)
Else
'If you are light loaded and drag drop then you will get a duplicated key error
Set currentNode = mTVW.Nodes.Add(Node.key, tvwChild, currentID, currentText)
currentNode.Tag = currentIdentifier
If mLoadImages Then
currentImage = Nz(rsTree.Fields(Image_Name_Field), "")
If currentImage <> "" Then currentNode.Image = currentImage
End If
RaiseEvent ExpandedBranch(currentNode)
bk = rsTree.Bookmark
Call addLiteBranch(currentID, currentIdentifier)
End If
rsTree.Bookmark = bk
rsTree.FindNext (strCriteria)
Loop
Exit Sub
errlbl:
If Not Err.Number = 35602 Then 'this is a duplicate key caused by drag drop and light loaded
MsgBox Err.Number & " " & Err.Description & " Key: " & currentID & " In Treeview Expand."
End If
End Sub
So it may be as simple as trying to incorporate these methods into my MS Forms wrapper class. But there may be additional dependencies that need to be coded.
Thanks for reply. I couldn't check that out since I was away from PC on which the project is located. I am quite sure that it will take more time to load the TV as the time is being taken in the loop which iteratively add one by one nodes. Since the approach you referred run the loop once to add all the nodes, and then again to add the parent.
So everyone knows what we are talking about there are two Treeviews that I know of that work in Access.
The most common is the ActiveX Treeview located in the MS CommonControls 6.0
The other was made by JKP and can be found here towards the bottom.
The JKP version is home made using MS Forms controls native to VBA. It is an extremely impressive amount of vba.
The advantage of the native MS Forms version is that it overcomes a lot of portability issues of using Active X.
Most of the methods and properties mirror the Active X control so it is relatively easy to build/convert a JKP treeview without learning the class methods and properties.
Let me explain the logic for the light load that I coded into my class that uses the ActiveX control.
Instead of fully populating the Tree with all the nodes, I only populate the top level visible nodes. For each visible node I also only load one child node and for each child node a single child node (recursivel)
In my demo in the below thread, I load a Treeview with 10K records and it loads instantaneously:
I have had some people ask questions on working with Hierarchical data and Tree Views, which often involves using recursion. If not familiar with recursion this is where a procedure repeatedly calls itself until hitting a condition where it is told to stop. This is used often where you have to...
www.access-programmers.co.uk
When the tree loads the following nodes are loaded and only one child (no sub nodes). View attachment 113528
The reason for the child node is that is how you get the Plus Sign. If there was only the 9 visible nodes, none of these nodes would appear expandable.
Assume you Click on Linda Powlowski. Additional nodes get loaded. The rest of linda's "child" nodes, and again one child node for each loaded child.
So originally Mallie was already loaded. It then loads Charlotte and the rest of Linda's child nodes.
So yes this same functionality could be added to the MSForms Class I built. Did this take me a long time to figure out? Yes. But since the two "controls" mirror each other I should be able to figure it out a lot faster. So when I get time I will see if I can add it. I always planned to write a complete helper class for this, but never got around.
Here is some of the light load logic. It only differs from the full load in that there is no loop to recurse all children. It recurses a single child only.
Code:
Public Sub addLiteBranch(parentIDValue As Variant, identifier)
On Error GoTo errLable
Dim strCriteria As String
Dim bk As String
Dim currentID As Variant
Dim currentText As String
Dim currentNode As Node
Dim currentImage As String
Dim ParentNode As Node
Dim strKey As String
Dim rsTree As DAO.Recordset
Set rsTree = Me.Recordset
strCriteria = Parent_ID_Field & " = '" & parentIDValue & "'"
'Since it is not a root node must determine the parent node. All node keys are "ID " & the primary key
Set ParentNode = Me.Nodes(parentIDValue)
rsTree.FindFirst (strCriteria)
'Only going to get the top node
If Not rsTree.NoMatch Then
currentID = rsTree.Fields(ID_Field)
currentText = rsTree.Fields(Node_Text_Field)
identifier = rsTree.Fields(Identifier_Field)
Set currentNode = Me.Nodes.Add(ParentNode, tvwChild, currentID, currentText)
currentNode.Tag = identifier
If mLoadImages Then
currentImage = Nz(rsTree.Fields(Image_Name_Field), "")
If currentImage <> "" Then currentNode.Image = currentImage
End If
bk = rsTree.Bookmark
Call addLiteBranch(currentID, identifier)
rsTree.Bookmark = bk
rsTree.FindNext (strCriteria)
End If
Exit Sub
errLable:
MsgBox Err.Number & " " & Err.Description & " In addBranch"
If MsgBox("Do you want to exit the loop?", vbYesNo, "Error In Loop") = vbYes Then
Exit Sub
Else
Resume Next
End If
End Sub
More importanly when you click on a node it does the same
Code:
Private Sub mTVW_Expand(ByVal Node As MSComctlLib.Node)
'Flush out any remaining nodes in this expanded level and liteload child level
'This procedure is associated with a light load. Occurs when you expand the branch
On Error GoTo errlbl
Dim strCriteria As String
Dim bk As String
Dim currentID As Variant
Dim currentText As String
Dim currentNode As Node
Dim ParentNode As Node
Dim currentIdentifier As String
Dim currentImage As String
Dim rsTree As DAO.Recordset
'Debug.Print Node.Text & " " & "Selected Node"
If Node.Children > 1 Then Exit Sub ' node has already been expanded
Set rsTree = Me.Recordset
strCriteria = Parent_ID_Field & " = '" & Node.key & "'"
rsTree.FindFirst (strCriteria)
Debug.Print strCriteria
If rsTree.NoMatch Then
' MsgBox "There is no record with a Parent ID of " & Node.key
End If
Do Until rsTree.NoMatch
currentID = rsTree.Fields(ID_Field)
Debug.Print currentID & " current"
currentText = rsTree.Fields(Node_Text_Field)
currentIdentifier = rsTree.Fields(Identifier_Field)
If Node.Child.key = currentID Then
Node.Child.Tag = currentIdentifier
RaiseEvent ExpandedBranch(Node.Child)
Else
'If you are light loaded and drag drop then you will get a duplicated key error
Set currentNode = mTVW.Nodes.Add(Node.key, tvwChild, currentID, currentText)
currentNode.Tag = currentIdentifier
If mLoadImages Then
currentImage = Nz(rsTree.Fields(Image_Name_Field), "")
If currentImage <> "" Then currentNode.Image = currentImage
End If
RaiseEvent ExpandedBranch(currentNode)
bk = rsTree.Bookmark
Call addLiteBranch(currentID, currentIdentifier)
End If
rsTree.Bookmark = bk
rsTree.FindNext (strCriteria)
Loop
Exit Sub
errlbl:
If Not Err.Number = 35602 Then 'this is a duplicate key caused by drag drop and light loaded
MsgBox Err.Number & " " & Err.Description & " Key: " & currentID & " In Treeview Expand."
End If
End Sub
So it may be as simple as trying to incorporate these methods into my MS Forms wrapper class. But there may be additional dependencies that need to be coded.
Thanks a lot for such a detailed reply. I will try to follow the TV coms control light load approach for MS Form method. If successful, I will post it here.
Went through my files and I see I started a demo for a JKP light load. I remember there is no event in that class equivalent to "Expand" which happens as you expect when expand a nodes branch. This would be required to do the light load like I described. I see I tried to add a custom event to mimic this. Not sure if I was unsuccessful or lost interest.
Code:
Friend Sub NodeClick(ByRef oCtl As MSForms.Control, ByRef cnode As clsNode)
'-------------------------------------------------------------------------
' Procedure : NodeClick
' Company : JKP Application Development Services (c)
' Author : Jan Karel Pieterse (www.jkp-ads.com)
' Created : 15-01-2013
' Purpose : Handles clicks on the treeview. Called from clsNode
'-------------------------------------------------------------------------
' PT also called from checkbox (label) click event in clsNode
Dim bFlag As Boolean
Dim lngViewable As Long
Dim cLastChild As clsNode
If oCtl.Name Like "Exp*" Then
bFlag = Not ActiveNode Is cnode
If bFlag Then
Set ActiveNode = cnode
End If
BuildRoot False
If cnode.Expanded Then
If Not cnode.ChildNodes Is Nothing Then
Set cLastChild = cnode.ChildNodes(cnode.ChildNodes.Count)
If Not NodeIsVisible(cLastChild, lngViewable) Then
If lngViewable > cnode.ChildNodes.Count Then
ScrollToView cLastChild, Top1Bottom2:=2
Else
ScrollToView cnode, Top1Bottom2:=1
End If
End If
End If
'------------------------------------------------------- MajP add ------------------------------------------------------------------
NodeExpanded cnode
End If
If bFlag Then
RaiseEvent Click(cnode)
End If
ElseIf oCtl.Name Like "CheckBox*" Then ' PT
' RaiseEvent for the checkbox moved to clsNode
RaiseEvent NodeCheck(cnode)
ElseIf oCtl.Name Like "Node*" Then
If Not ActiveNode Is cnode Then
Set ActiveNode = cnode
Else
SetActiveNodeColor
End If
RaiseEvent Click(cnode)
End If
End Sub
I added that line where it checks the click. So that line calls this function that raises the expanded event.
Code:
Public Function NodeExpanded(cnode As clsNode)
RaiseEvent NodeExpanded(cnode)
End Function
So if this event I added works then should be able to recreate the light load idea. If not, I not sure what if any solution exists.
Went through my files and I see I started a demo for a JKP light load. I remember there is no event in that class equivalent to "Expand" which happens as you expect when expand a nodes branch. This would be required to do the light load like I described. I see I tried to add a custom event to mimic this. Not sure if I was unsuccessful or lost interest.
Code:
Friend Sub NodeClick(ByRef oCtl As MSForms.Control, ByRef cnode As clsNode)
'-------------------------------------------------------------------------
' Procedure : NodeClick
' Company : JKP Application Development Services (c)
' Author : Jan Karel Pieterse (www.jkp-ads.com)
' Created : 15-01-2013
' Purpose : Handles clicks on the treeview. Called from clsNode
'-------------------------------------------------------------------------
' PT also called from checkbox (label) click event in clsNode
Dim bFlag As Boolean
Dim lngViewable As Long
Dim cLastChild As clsNode
If oCtl.Name Like "Exp*" Then
bFlag = Not ActiveNode Is cnode
If bFlag Then
Set ActiveNode = cnode
End If
BuildRoot False
If cnode.Expanded Then
If Not cnode.ChildNodes Is Nothing Then
Set cLastChild = cnode.ChildNodes(cnode.ChildNodes.Count)
If Not NodeIsVisible(cLastChild, lngViewable) Then
If lngViewable > cnode.ChildNodes.Count Then
ScrollToView cLastChild, Top1Bottom2:=2
Else
ScrollToView cnode, Top1Bottom2:=1
End If
End If
End If
'------------------------------------------------------- MajP add ------------------------------------------------------------------
NodeExpanded cnode
End If
If bFlag Then
RaiseEvent Click(cnode)
End If
ElseIf oCtl.Name Like "CheckBox*" Then ' PT
' RaiseEvent for the checkbox moved to clsNode
RaiseEvent NodeCheck(cnode)
ElseIf oCtl.Name Like "Node*" Then
If Not ActiveNode Is cnode Then
Set ActiveNode = cnode
Else
SetActiveNodeColor
End If
RaiseEvent Click(cnode)
End If
End Sub
I added that line where it checks the click. So that line calls this function that raises the expanded event.
Code:
Public Function NodeExpanded(cnode As clsNode)
RaiseEvent NodeExpanded(cnode)
End Function
So if this event I added works then should be able to recreate the light load idea. If not, I not sure what if any solution exists.
I am using tree view loader class to load the tree view from the table. If I am not incorrect, I have to add procedures in tree view loader class for light load.
Yes, but as I explained it is a multi step process.
You need to add a loadlightbranch which will add just the one level of child nodes on load. But then you have to trap the expanded event to start flushing out the lower level (one level deep). This means your class also needs a reference to the recordset which you would have to persist in the class. I tested the expand event that I added so I know I can do this. I just need some time. Unfortunately it is Tax time so got to unfortunately take care of that first and then I am on travel. Might be a while before I can dig in, but already started. Basically if I am going to do this, I will end up doing a complete "wrapper class" like I have for the ActiveX.
Yes, but as I explained it is a multi step process.
You need to add a loadlightbranch which will add just the one level of child nodes on load. But then you have to trap the expanded event to start flushing out the lower level (one level deep). This means your class also needs a reference to the recordset which you would have to persist in the class. I tested the expand event that I added so I know I can do this. I just need some time. Unfortunately it is Tax time so got to unfortunately take care of that first and then I am on travel. Might be a while before I can dig in, but already started. Basically if I am going to do this, I will end up doing a complete "wrapper class" like I have for the ActiveX.
I am quite sure that it will take more time to load the TV as the time is being taken in the loop which iteratively add one by one nodes. Since the approach you referred run the loop once to add all the nodes, and then again to add the parent.
It would have been an interesting experiment because the code is very short and simple to adapt, here's a more declarative approach:
Java:
'// #############################################################################################
'// Email: edgarfreelancing@gmail.com
'// Last update: 24/03/2024
'//
'// Description:
'// Populates a TreeView control with hierarchical data from a Recordset without recursion.
'//
'// Parameters:
'// rs: Recordset containing hierarchical data.
'// tvw: TreeView control to populate (Microsoft treeview is preferred)
'// primaryKeyName: Name of the primary key field in the Recordset.
'// nodeText: Name of the field in the Recordset to be displayed as node text.
'// parentFieldName: Name of the field in the Recordset containing parent node IDs.
'//
'// Example usage:
'// PopulateTreeView myRS, Me.MyTreeViewControl.Object, "EmployeeID", "FullName", "SupervisorID"
'// #############################################################################################
Public Sub PopulateTreeView(rs As Recordset, tvw As TreeView, primaryKeyName As String, nodeText As String, parentFieldName As String)
' 1st step: add all nodes
Do While Not rs.EOF
tvw.Nodes.Add , , "k" & rs.Fields(primaryKeyName).Value, rs.Fields(nodeText).Value
rs.MoveNext
Loop
' 2nd step: assign parents to all nodes
rs.MoveFirst
Do While Not rs.EOF
If rs.Fields(parentFieldName).Value <> 0 Then
Set tvw.Nodes("k" & rs.Fields(primaryKeyName).Value).Parent = tvw.Nodes("k" & rs.Fields(parentFieldName).Value)
End If
rs.MoveNext
Loop
End Sub
@Pac-Man
I figured out the light load. See demo. It appears to be extremely fast in the test case of 10k nodes.
This is part of what I am calling the JKP_Loader. I tried to incorporate this into a similar wrapper as what I did for the Active X, but ran into issues. This loader is not quite as user friendly as the complete wrapper class and does not provide all the functions. The wrapper class is something I hope to build and you can see the start in here..
The Demo has 10k records and loads near instantaneously. There are some things I think I can do to make this even faster. I am still loading more than I think I need, but not sure if that is an easy fix. I believe there was a reason I did it this way originally.
It would have been an interesting experiment because the code is very short and simple to adapt, here's a more declarative approach:
I ran a few tests and it is hard to compare a standard full load to this version of the full load. I think your version is more unstable. Either way both cases are bad for any large tree. I could get the 10k to load in about 45 seconds to a minute for 10k. I tried your version and sometimes it ran as fast or slightly faster, other times much slower, and often it crashed. I think the problem is that these trees are unstable with too much visible nodes and they get unpredictable. This method is good for small trees, and I have used it for some simple demos.
Bottom line the light load is order of magnitude faster simply because you are not loading everything. The tree does not get unstable. Now this also depends on the shape of the tree. A tree with lots of levels and few nodes per level is far faster than a tree with few levels and lots of nodes per level using the light load.
I do think in general the ActiveX loads faster than the JKP MSFORMS version.
So how fast are 10k records loaded? (or actually not-loaded)
From the top of the Form it said it took .16 seconds. It created the 100 visible nodes, and an additional 250 nodes not shown (350 total). These are what give you the Expanders. The total controls represents the lines, puls sign, and box to fake a tree. This is why it is critical to do a light load IMO. Everything you see is created in a MS Forms control.
@Pac-Man
I figured out the light load. See demo. It appears to be extremely fast in the test case of 10k nodes.
This is part of what I am calling the JKP_Loader. I tried to incorporate this into a similar wrapper as what I did for the Active X, but ran into issues. This loader is not quite as user friendly as the complete wrapper class and does not provide all the functions. The wrapper class is something I hope to build and you can see the start in here..
The Demo has 10k records and loads near instantaneously. There are some things I think I can do to make this even faster. I am still loading more than I think I need, but not sure if that is an easy fix. I believe there was a reason I did it this way originally.
I ran a few tests and it is hard to compare a standard full load to this version of the full load. I think your version is more unstable. Either way both cases are bad for any large tree. I could get the 10k to load in about 45 seconds to a minute for 10k. I tried your version and sometimes it ran as fast or slightly faster, other times much slower, and often it crashed. I think the problem is that these trees are unstable with too much visible nodes and they get unpredictable. This method is good for small trees, and I have used it for some simple demos.
Bottom line the light load is order of magnitude faster simply because you are not loading everything. The tree does not get unstable. Now this also depends on the shape of the tree. A tree with lots of levels and few nodes per level is far faster than a tree with few levels and lots of nodes per level using the light load.
I do think in general the ActiveX loads faster than the JKP MSFORMS version.
Thanks a lot @MajP for your time and effort you put into it. I am on Eid holidays and don't have PC to check till next Monday. I'll check it and inform you as soon as I check. If it works so much fast for 10k records then it will work for my case faster. Thanks again and God bless you.
No problem. I was always planning to get around to doing this. This approach is a game-changer for large trees in either the active x or JKP MSForms version.
I think your version is more unstable. Either way both cases are bad for any large tree. I could get the 10k to load in about 45 seconds to a minute for 10k. I tried your version and sometimes it ran as fast or slightly faster, other times much slower, and often it crashed.
Thanks for the testing, I'm wondering if I could test for a certain parent depth before the parent assignment "pass". Maybe that could help avoid the instability of the control when too many nodes are loaded.
Thanks for the testing, I'm wondering if I could test for a certain parent depth before the parent assignment "pass". Maybe that could help avoid the instability of the control when too many nodes are loaded.
Maybe, but It is a waste of time. The light load concept is so many orders of magnitude faster on a large tree. You can probably figure out a non recursive lite load concept and I would put my efforts into that. It should be relatively simple. My class is probably more complicated than needed because it is persisting the recordset.
1. Render the parent nodes
2. Loop the parent nodes and add one child node
When you expand a node
trap the expand method
3. Add the rest of the child nodes to the expanded node and one child per child node.
Are you able to keep the tree from updating? The big problem I found with that your version start screen updating. I tried to turn this off, but could not. I thought you could hide the nodes, but that seems to be a read only property. Not sure what good that is. I tried to keep the node from expanding.
In the demo you can go through the 3 choices. Lite Load, Full Recursive, and your 2 pass version. I think the full recursive version is still going to be faster than the two pass because the nodes are not expanded and few nodes are rendered to the screen. My full version loads in about a minute, but I am unable to successfully load yours most of the time.
If I set the linestyle property of the treeview to tvwRootLines and set the expanded property to false to all nodes after they're loaded, you can. But they're still loaded, so...
In my testing, I vaguely recall there was another property to show it collapsed, but I'm not sure if I'm confusing it with another thing.
Pick Change Tree Demo.
The three buttons are the Lite Load, a recursive full load, and then the two pass non-recursive method. The latter two may or may not complete and crash the db. It may not be a completely fair test so you may want to modify the 2 pass method to not update the screen. Regardless, if you do not want to use a recursive load then you could code a non-recursive lite load.