Link to home
Start Free TrialLog in
Avatar of Éric Moreau
Éric MoreauFlag for Canada

asked on

VB2005 + TreeView + TreeViewNodeSorter property

Hi

I am giving a try to the new TreeViewNodeSorter property of the Treeview control in VB 2005.

The help files says:
"After the TreeViewNodeSorter is set, if the Sorted property is set to true, the nodes will be sorted in alphabetical order."

But nothing is changed in my treeview when I set Sorted to True after having set the TreeViewNodeSorter to a custom compare class.

How can I revert back to a plain sort?
Avatar of Éric Moreau
Éric Moreau
Flag of Canada image

ASKER

And I have tried to set the TreeViewNodeSorter property to Nothing before setting Sorted to True and that did not fix the problem!
Eric,

Can you show me the TreeViewNodeSorter implementation, please?

Bob
I took the one from the help file and change only the setting to the TreeViewNodeSorter:

' Set the TreeViewNodeSorter property to a new instance
' of the custom sorter.
Private Sub button1_Click(ByVal sender As Object, _
    ByVal e As EventArgs) Handles button1.Click

    With TreeView1
          .BeginUpdate
          .TreeViewNodeSorter = New NodeSorter()
          .EndUpdate
    End With
End Sub 'button1_Click

' Create a node sorter that implements the IComparer interface.

Public Class NodeSorter
    Implements IComparer
   
    ' Compare the length of the strings, or the strings
    ' themselves, if they are the same length.
    Public Function Compare(ByVal x As Object, ByVal y As Object) _
        As Integer Implements IComparer.Compare
        Dim tx As TreeNode = CType(x, TreeNode)
        Dim ty As TreeNode = CType(y, TreeNode)
       
        If tx.Text.Length <> ty.Text.Length Then
            Return tx.Text.Length - ty.Text.Length
        End If
        Return String.Compare(ty.Text, tx.Text)

    End Function
End Class
I did this small test:

        Dim root As TreeNode = Me.TreeView1.Nodes.Add("Root")

        Dim parent As TreeNode = root.Nodes.Add("Parent")

        parent.Nodes.Add("Child 5")
        parent.Nodes.Add("Child 4")
        parent.Nodes.Add("Child 3")
        parent.Nodes.Add("Child 2")
        parent.Nodes.Add("Child 1")

        root.ExpandAll()

        Me.TreeView1.Sort()

So, what you need is to call the Sort method.  If you need to implement a non-standard sort order, such as case-insensitive, descending strings, then you would implement a TreeViewNodeSorter instance, but you would still need to call the Sort method to perform the sort.

Bob
I have added 2 buttons to a form that contains the treeview.

the first button set the Sorted property to True

the second button set .TreeViewNodeSorter property to New NodeSorter()

When I run, I can click the first button and the list sort alphabetically. I can then click the second button and the nodes are sorted based on their length (as the Compare method sample does). BTW, all that is working without ever calling the Sort() method.

Finally, if I click the first button, nothing happens. As I read from the help file, it should revert back to the alphabetical order.
Confusion:  I don't see a Sorted property.  Is this ASP.NET or WinForms?  Help me get to the same place that you are.

Bob
Windows forms.

The Sorted property does not appear in the Intellisense but it is working. It is even documented in the help.
Crap!!!  More hidden properties that I need to discover :(

Did you say that it is working now?

Bob
not at all.

What I try to do is to be able to switch from one method to the other. If we refer to the help, it should be doable:
"After the TreeViewNodeSorter is set, if the Sorted property is set to true, the nodes will be sorted in alphabetical order."
Ok, that looks like a bug with the example.  I changed the NodeSorter to this, and it works:

    Public Class NodeSorter
        Implements IComparer

        ' Compare the length of the strings, or the strings
        ' themselves, if they are the same length.
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
            Dim tx As TreeNode = CType(x, TreeNode)
            Dim ty As TreeNode = CType(y, TreeNode)

            If tx.Text.Length <> ty.Text.Length Then
                Return tx.Text.Length - ty.Text.Length
            End If
            Return String.Compare(tx.Text, ty.Text)

        End Function
    End Class

I transposed tx.Text and ty.Text:

            Return String.Compare(tx.Text, ty.Text)

instead of

            Return String.Compare(ty.Text, tx.Text)

Bob
Now, if you want the default sorting of ascending, case-sensitive, the TreeView already implements a node sorter.

Bob
Sadly, I can't try it right now (not before tonight).

Are you telling me that if I replace this single line, I will be able to use both buttons (default Sorted property and the new TreeViewNodeSorter property) one after the other and both will work?

The case that was not working for me is:
-click button1 (Sorted = True) - Sorts OK - nodes sorted alphabetically
-click button2 (TreeViewNodeSorter = New NodeSorter()) - Sorts OK - nodes sorted by length
-click button1 (Sorted = True) - Does nothing - nodes still sorted by length
What I am telling you is that I used the NodeSorter that you showed me (that I also saw from MSDN help), and it didn't work for me until I switched the order of those two text elements in the String.Compare call.  I was using the Sorted = True property setting.

Bob
and are you able to revert to a simple default alphabetical order after you set the TreeViewNodeSorter?

Your nodes are not a perfect test set. You should try with:

        parent.Nodes.Add("Child 55555555")
        parent.Nodes.Add("Child 4")
        parent.Nodes.Add("Child 333")
        parent.Nodes.Add("Child 22")
        parent.Nodes.Add("Child 1111")

If you sort only with Sorted, since it is alphabetical, you should get:
Child 1111
Child 22
Child 333
Child 4
Child 55555555

Then if you sort with TreeViewNodeSorter, you should see the node sorted from the longest to the shortest like this:
Child 55555555
Child 1111
Child 333
Child 22
Child 4

I would then lile to return to first sort (alphabetical order).
When I ran the test without the NodeSorter, I get this:

Child 1111
Child 22
Child 333
Child 4
Child 55555555

With the NodeSorter, I get this:

Child 4
Child 22
Child 333
Child 1111
Child 55555555

This appears to be a numeric sort order, and not a string sort order.

Bob
>>With the NodeSorter, I get this:
>>This appears to be a numeric sort order

It is from shortest string to longest string because you change
Return String.Compare(ty.Text, tx.Text)
to
Return String.Compare(tx.Text, ty.Text)

up to here, everything is OK.

Now what if you try get back to the first alphabetical sort to return to :
Child 1111
Child 22
Child 333
Child 4
Child 55555555
Nope, changing the order of tx.Text and ty.Text didn't affect the order of the sort, because it never gets to that, since it always passes this check:

    If tx.Text.Length <> ty.Text.Length Then

I tried this:

   parent.Nodes.Add("Child 55555555")
   parent.Nodes.Add("Child 4")
   parent.Nodes.Add("Child 333")
   parent.Nodes.Add("Child 22")
   parent.Nodes.Add("Child 1111")
   parent.Nodes.Add("Child 1011")

and got this with Return String.Compare(tx.Text, ty.Text):

Child 4
Child 22
Child 333
Child 1011
Child 1111
Child 55555555

and this with Return String.Compare(ty.Text, tx.Text):

Child 4
Child 22
Child 333
Child 1111
Child 1011
Child 55555555

Bob
ok but my main point here is not the Compare class, it is how to get back to the first alphabetical sort to return to :
Child 1111
Child 22
Child 333
Child 4
Child 55555555

I want the user to be able to switch between the default sorting and a custom sorting.
You didn't had time or you haven't found any solution? That may be a bug or an error in the help file.
Just doing a little investigation with Reflector.

1) Here is the property

    Public Property TreeViewNodeSorter As IComparer
      Get
            Return Me.treeViewNodeSorter
      End Get
      Set(ByVal value As IComparer)
            If (Not Me.treeViewNodeSorter Is value) Then
                  Me.treeViewNodeSorter = value
                  If (Not value Is Nothing) Then
                        Me.Sort
                  End If
            End If
      End Set
End Property

2) When you set the TreeViewNodeSorter to NodeSorter, the control calls the Sort method

3) The Sorted property does this:

Public Property Sorted As Boolean
      Get
            Return Me.treeViewState.Item(128)
      End Get
      Set(ByVal value As Boolean)
            If (Me.Sorted <> value) Then
                  Me.treeViewState.Item(128) = value
                  If ((Me.Sorted AndAlso (Me.TreeViewNodeSorter Is Nothing)) AndAlso (Me.Nodes.Count >= 1)) Then
                        Me.RefreshNodes
                  End If
            End If
      End Set
End Property

4) RefreshNodes does this:

Private Sub RefreshNodes()
      Dim nodeArray1 As TreeNode() = New TreeNode(Me.Nodes.Count  - 1) {}
      Me.Nodes.CopyTo(nodeArray1, 0)
      Me.Nodes.Clear
      Me.Nodes.AddRange(nodeArray1)
End Sub

5) I am trying to find in all that code how it is sorted by default.  It is buried in there somewhere.

Bob
The Sort method does this:

   Public Sub Sort()
      Me.Sorted = True
      Me.RefreshNodes()
   End Sub

How does that work if you haven't set the TreeViewNodeSorter?

Bob
>>How does that work if you haven't set the TreeViewNodeSorter?

Nodes are sorted alphabetically. In my scenario, it is my button1.
Right, but internally in the control I can't see how nodes are sorted just by clearing and readding nodes.

Bob
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Bob, you just found it.

I have tried calling the Sort method and setting the TreeViewNodeSorter property to nothing but never togheter!

Here is how the code needs to look like:
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'This will sort the string from the shortest to the longest
        With TreeView1
            .BeginUpdate()
            .TreeViewNodeSorter = Nothing
            .Sorted = True
            .Sort()
            .EndUpdate()
        End With
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'This will sort the string from the shortest to the longest
        With TreeView1
            .BeginUpdate()
            .TreeViewNodeSorter = New NodeSorter()
            .EndUpdate()
        End With
    End Sub

The interesting thing the first time is Sorted = True and Sort will cause the sort to happen twice.

Bob
here is another way of doing for which we don't need to call the Sort method:

        'This will sort the string from the shortest to the longest
        With TreeView1
            .BeginUpdate()
            If .TreeViewNodeSorter IsNot Nothing Then
                .Sorted = False
                .TreeViewNodeSorter = Nothing
            End If
            .Sorted = True
            .EndUpdate()
        End With