Solved Majp Treeview (1 Viewer)


Registered User.
Local time
Today, 01:13
Oct 14, 2019
I use the treeview control extensively. Thank you very much, Majp!
However, I'm stumped at how to select the nodes I want. I use the PK to select individual records,
but using it for a print selection, I'd like to be able to select the top levels and
all under them would be selected. Or select a level under that and only all succeeding items
under that level would select. I know the node key, the node level, the identifier and all that but
still don't know how to select what I need. Suggestions? My levels are Drawers (DR), Divisions (DI),
Folders (FI), Items (IT) with check boxes. Using VBA, how do I select only various sections?
If you use @MajP it will alert him that he's mentioned in the thread.
I have a method in there already called iterateBranch that may be useable. I probably should build a function as well that would return all nodes that fall under that given node. If you call iterateBranch and pass in a parent node it will go through all sublevels below it and raise an event for each node found. You can then trap the event and do something for each node.
So on your form you will have the event
Can you provide a little more detail on what you want to do.
If you only want the next lower level, then you can simply use the childNodes collection of a node.

So on your form you would have something like
Public WithEvents TVW As TreeviewForm
Private Sub TVW_NodeIterated(Node As MSComctlLib.Node)
  do something here
End Sub

Then when you call IterateBranch and pass in a top level node you can then do something for each node below the passed in node.
If this is want you are looking for then I can make it cleaner and build a function
This would pass you back a collection of all nodes below a passed node.
Of course... When I select a checkbox I want all boxes below to be checked as well. Then I'd use that as my recordsource for the report. So If you're printing the whole thing you'd check the top level. If only one section, the level above that section.
your function sounds exactly right and I think very useful for others?
Depending on the level, it would need to go to the end... not just the immediate children
I am back from travel, and was wondering why I would not have this feature. In fact I already did, but the naming convention was not clear on the function. Usually all my functions start with "Get". In the demo I called this function "ExpandedChildNodes" and at first I did not even see this, although. I just changed the name to "GetDescendants". To me this is clearer. You pass in a node and you get its descendants which is all children and sub children. To see how it works I added a button and this code

Private Sub CmdGetDescendants_Click()

  Dim myDescendants As Collection
  Dim myNode As Node
  Dim I As Integer
  Dim strOut As String
  MsgBox "This demonstrates getting all the children and sub childrent of a parent node."
  If Not TVW.Selectednode Is Nothing Then
    Set myDescendants = TVW.GetDescendants(TVW.Selectednode)
    For I = 1 To myDescendants.Count
       Set myNode = myDescendants(I)
       strOut = strOut & " PK:" & TVW.getNodePK(myNode) & " " & myNode.Text & " Level: " & TVW.GetNodeLevel(myNode) & vbCrLf
    Next I
    MsgBox "The returned Descendants for the selected node " & TVW.Selectednode.Text & " is: " & vbCrLf & strOut
    MsgBox "No node selected"
  End If
  End Sub


The renamed function in the class. As you can see this function is recursive

Public Function GetDescendants(theNode As Node, Optional theDescendants As Collection = Nothing) As Collection
  'This gets all children and sub children.  These are a nodes Descendants
  Dim nod As Node
  If theDescendants Is Nothing Then Set theDescendants = New Collection
  For Each nod In childNodes(theNode)
    theDescendants.Add nod
    If Not nod.Child Is Nothing Then
      GetDescendants nod, theDescendants
    End If
  Next nod
  Set GetDescendants = theDescendants
End Function
I included the new class module or you can simply replace ExpandedChildNodes with the above function


Wouldn't the event that calls GetDescendants be
Private Sub mTVW_NodeCheck(ByVal Selectednode As MSComctlLib.Node)?
You don't have any examples in your treeview of checkboxes so I'm winging it here but I can't seem to get to the event.
I was unclear what you wanted to do once you returned the child nodes. Do you want to check them also? When you check a node what do you want to happen? There is a demo on one of the other forms with checks. I will demo the above using the node check event.
What I want to do, using checkboxes, is pull up a dataset for printing. Not find individual records but a selection of records.
To simplify.... I have drawers. I have divisions. I have folders. I have topics. I need a print out of each drawer, not all drawers. I need a report of a particular topic. Not all topics, etc.
Can you take a screen shot?
Is it like
----- Divisions

To simplify.... I have drawers. I have divisions. I have folders. I have topics. I need a print out of each drawer, not all drawers. I need a report of a particular topic. Not all topics, etc
That description does not sound like I think I provided. I was thinking you select a Node and then print all records in the family below the node.
The above description sounds different. Let me send the demo with checks and maybe can use that to explain. How you want to interface with it and make it work.
I think that's exactly what I want. Select the drawer and all divisions, folders, & topics below it print. Select the division and all folders under it print. It's extremely hierarchal (?) but in this particular place I don't want individual record selection, just parent and children.
For Demo purposes.
Open form
1. Select Expand tree
2. Select Show Checks
3. Check Steve Buchanan
4. Select Print Checks
5. Unselect Steve Buchanan

I have done some modifications to the Class. This may break some things if you were capturing the treeview events. I was returning the Primary Key and really should just be returning the node. I was making it less flexible and not more flexible. If you bring in the class then hit compile. You may have some modification to do. I added the new method to checkdescendants and uncheckdescendants and clearAllchecks. Also you have the procedures to implement this in other ways using the getDescendantsMethod. You could have a button on the side with simply "Print Selected Node" and "Print Selected Node and Descendants." You can then do away with the checks. But this checks is pretty flexible. You can picks any combination of records to print.



Last edited:
YES! YES! You nailed it! Thank you so much. I wasted so much time here before asking you...
Me, again. The treeview works exactly right to select the records I want. I don't know enough to get it to print what it shows.

This is my report button and it falls apart when more than one Parent node is selected. I can work on that but I suspect I'm missing the ease of your print code. How do I convert the selection into a recordset?
Private Sub btnPrint_Click()
    Dim strReport As String
    Dim strParent As String
If Not TVW.SelectedNode Is Nothing Then
    strParent = "" & TVW.SelectedNode.Key & ""
End If

If strParent = "DR2" Then
    strReport = "rptAssets"
Else: strReport = "rptFiles"
End If

DoCmd.OpenReport _
    ReportName:=strReport, _
    View:=acViewPreview, _
    WhereCondition:="Parent = " & strParent
 DoCmd.Close acForm, Me.Name, acSaveYes
End Sub

This is your report button:
Private Sub cmdPrint_Click()
  MsgBox "This simulates the printing of checked objects"
  Dim PK As Variant
  Dim strOut As String
  Dim nd As Node
  For Each nd In TVW.Nodes
    If nd.Checked Then
      MsgBox "Do something here to print the node. For: PK " & TVW.getNodePK(nd) & " " & nd.Text
    End If
  Next nd
End Sub
Not sure I understand what you are asking. I interpret this to mean you may have something like multiple drawers (where x is a check) selected. Do you want to create a single report for these drawers or two reports (on for drawer A and one for drawer b)

X DrawersA
----- Divisions
X DrawersB
----- Divisions

I do not know how your tables are reports are set up so I am guessing. But my guess is that you are not printing out an individual report for each node. That was my first interpretation. So you want to create a report for two Drawers. Is the report itself is a grouped report that includes divisions, folders, topics? Something like.

ParentDrawer = "DrawerA" or ParentDrawer = "DrawerB"

If you were doing this manually what criteria would you pass to the report/s?
I want to print everything for in the front of each drawer. I want to print sections for loan or reference... I want to use the thing in different places for different purposes... I've attached a small version of work in progress



I am still a little lost. None of the reports are grouped on the drawer or the topic or include them in the query. If I clicked the above drawer and selected the topics and items, what report/s gets printed.
I would think you have a grouped report.
Then I would simply print some report where Drawer = TopDrawer, West Cabinet
But that would print all.
If I wanted to pick specific items to print in a report then I would have to loop the selected items and if they are at the Item level (check the indentifier) then I would have to build a sql string in the loop.
something the resolves to
Where ItemID in (123, 789, 24, ....)
for each item.

Sorry I am still lost on what would be printed in which report, based on the above checks.

Users who are viewing this thread

Top Bottom