Éric Moreau
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?
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?
Eric,
Can you show me the TreeViewNodeSorter implementation, please?
Bob
Can you show me the TreeViewNodeSorter implementation, please?
Bob
ASKER
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
' 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("Ro ot")
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
Dim root As TreeNode = Me.TreeView1.Nodes.Add("Ro
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
ASKER
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.
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
Bob
ASKER
Windows forms.
The Sorted property does not appear in the Intellisense but it is working. It is even documented in the help.
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
Did you say that it is working now?
Bob
ASKER
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."
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
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
Bob
ASKER
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
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
Bob
ASKER
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).
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
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
ASKER
>>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
>>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
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
ASKER
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.
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.
ASKER
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(nodeArra y1)
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
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)
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
Me.Nodes.Clear
Me.Nodes.AddRange(nodeArra
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
Public Sub Sort()
Me.Sorted = True
Me.RefreshNodes()
End Sub
How does that work if you haven't set the TreeViewNodeSorter?
Bob
ASKER
>>How does that work if you haven't set the TreeViewNodeSorter?
Nodes are sorted alphabetically. In my scenario, it is my button1.
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
Bob
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
Bob
ASKER
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
'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
ASKER