2

We have a legacy application written in VB6 using the default Microsoft TreeView control to display hierarchical data. Since there's a lot of information in this TreeView we thought about implementing a small filter/search possibility for the users.

The first attempt was to toggle the visibility and only make nodes (and their parents) visible, that match the defined search. But that was impossible, since the default Microsoft TreeView control doesn't allow their nodes to be visible or invisible.

So the second attempt is to traverse all the nodes in the tree and ensure a node's visibility when it matches the defined search (and stop traversing). When the found node isn't the one the user is looking for he can "continue" the search and look for the next node that matches his criteria.

This works quite good so far, except for one little problem. The search doesn't work from top to bottom, since the tree is filled with data in random sort order and then sorted by setting the Sorted property of each node (legacy code). So the search is going through the randomly added nodes and the found nodes "jump" between the top level nodes (the Nodes collection of the TreeView contains all the nodes in the order they were added, not just the top level nodes and not in the order they are presented to the user).

Is there any way to get all the nodes of the tree in the order they are shown to the user? I don't like to change the legacy code that is filling the tree with data, and sorting the data before it is added to the TreeView might affect performance.

Please be aware that I'm talking about an application written in VB6, so there's no such thing as LINQ to traverse the nodes in the desired order.

Here's my searching code so far:

Private Sub cmdSearch_Click()
    Dim oMatch As Node

    Set oMatch = GetNextMatch

    If Not (oMatch Is Nothing) Then
        oMatch.EnsureVisible
        oMatch.Selected = True
        Me.TreeView1.SelectedItem = oMatch
        Me.TreeView1.SetFocus
    End If

End Sub

Private Function GetNextMatch() As Node
    Dim lIndex As Long
    Dim oResult As Node

    For lIndex = mlLastFoundIndex + 1 To Me.TreeView1.Nodes.Count
        If IsMatch(Me.TreeView1.Nodes(lIndex).Text) Then
            Set oResult = Me.TreeView1.Nodes(lIndex)
            mlLastFoundIndex = lIndex
            Exit For
        End If
    Next lIndex

    Set GetNextMatch = oResult
End Function

Private Sub txtSearch_Change()
    mlLastFoundIndex = 0
End Sub
Nostromo
  • 1,177
  • 10
  • 28
  • 3
    It sounds like you've already figured out a root cause of why this is hard - that the treeview itself is being used as the 'master' data structure for these operations. If instead you had a separate data structure you could implement anything you wanted and then just copy the results into the treeview. I can see where that might be a lot of work to an existing app, but sometimes that sort of refactoring turns out to be easier in the long run... less lurching from special case to special case (all of which are hard to anticipate) – StayOnTarget Mar 07 '19 at 12:23
  • I agree with DaveInCaz but could you post the "search" code you're working with? I've worked with the control before and it can be quirky. The nodes I believe are indexed starting with 0. – Jimmy Smith Mar 07 '19 at 16:13
  • No, the indexing with the `Nodes` starts with 1. I put the search code in the question. I'm looking for something that delivers the `Me.TreeView1.Nodes` in the order the nodes are presented to the user. – Nostromo Mar 08 '19 at 05:58

1 Answers1

0

Rather than traversing the TreeView nodes collection by integer, you should use the Child and Next properties to accomplish what you're asking. The following code will print the children of the selected node in the order they appear in the TreeView control:

Private Sub Command3_Click()
  If Not TreeView1.SelectedItem Is Nothing Then
    PrintNodesInSortedOrder TreeView1.SelectedItem
  End If
End Sub

Private Sub PrintNodesInSortedOrder(ParentNode As MSComctlLib.Node)
  Dim Nod As MSComctlLib.Node

  Set Nod = ParentNode.Child
  Do While Not Nod Is Nothing
    Debug.Print Nod.Text
    Set Nod = Nod.Next
  Loop

End Sub