I have the need to sort a custom collection that contains sub classes. For example,
I've structured my classes like this:
Public Class Machine
Private _Model as Model
Public Property vModel as Model
Get
Return _Model
End Get
Set (ByVal Value as Model)
_Model = Value
End Set
End Property
Public Sub New()
_Model = new Model
End Sub
End Class
Public Class Model
Private _ModelName as String
Public Property ModelName as String
Get
Return _ModelName
End Get
Set (ByVal Value as String)
_ModelName = Value
End Set
End Property
Public Sub New()
_ModelName = ""
End Sub
End Class
I then made a collection of Machine objects like this:
Public Class MachineList
Inherits CollectionBase
Default Public Property Item(ByVal Index As Integer) As Machine
Get
Return CType(List.Item(Index), Machine)
End Get
Set(ByVal Value As Machine)
List.Item(Index) = Value
End Set
End Property
Public Function Add(ByVal Item As Machine) As Integer
Return List.Add(Item)
End Function
Public Sub Remove(ByVal Item As Machine)
List.Remove(Item)
End Sub
End Class
Pretty straight-forward, right?
Well, there was no easy way to sort this. It's one of the Unsortable classes. As I was searching through the net, I found this article by Jan Tielens, which claims to be able to sort the unsortable collection. In normal circumstances, it does. However, I have nested custom classes. His solution only works on the first level.
So, I set to work trying to figure out a way, using Reflection, to pass in the Property name that I wanted to compare. (ie: "Model.ModelName"). Well, I parsed the Property Name at the Period, and used reflection to get the values in string form. Then, determined if they were numeric or not, and I compared them, and returned 0, -1, or 1, depending on the values. I then took my routine, and stuck it into Jan's Compare Routine. Voila, a custom nested class sorter. It'll go as deep as you want it to go.
At this point, it's dirty. I'll be working on a way to clean it up a bit, but it does what I want it to.
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _
Implements System.Collections.IComparer.Compare
Dim i() As Object
Dim c As Integer = 0
Dim tempSP() As String = PropertyToSort.Split(".")
Dim v(tempSP.GetUpperBound(0)) As Object
Dim b(tempSP.GetUpperBound(0)) As Object
Dim temp As String
Dim m As Machine = CType(x, Machine)
Dim n As Machine = CType(y, Machine)
For Each temp In tempSP
v(c) = New Object()
b(c) = New Object()
Select Case temp
Case "Model"
v(c) = m.GetType.GetProperty(temp).GetValue(m, i)
b(c) = n.GetType.GetProperty(temp).GetValue(n, i)
Case Else
If IsNothing(v) Then
v(c) = m.GetType.GetProperty(temp).GetValue(m, i)
b(c) = n.GetType.GetProperty(temp).GetValue(n, i)
Else
If c > 0 Then
v(c) = CStr(v(c - 1).GetType.GetProperty(temp).GetValue(v(c - 1), i))
b(c) = CStr(b(c - 1).GetType.GetProperty(temp).GetValue(b(c - 1), i))
Else
v(c) = CStr(m.GetType.GetProperty(temp).GetValue(m, i))
b(c) = CStr(n.GetType.GetProperty(temp).GetValue(n, i))
End If
End If
End Select
c = c + 1
Next
c = c - 1
If IsNumeric(b(c)) And IsNumeric(v(c)) Then
If SortOrder = Windows.Forms.SortOrder.Ascending Or SortOrder = Windows.Forms.SortOrder.None Then
If CLng(v(c)) > CLng(b(c)) Then
Return 1
End If
If CLng(v(c)) = CLng(b(c)) Then
Return 0
End If
If CLng(v(c)) < CLng(b(c)) Then
Return -1
End If
Else
If CLng(v(c)) < CLng(b(c)) Then
Return 1
End If
If CLng(v(c)) = CLng(b(c)) Then
Return 0
End If
If CLng(v(c)) > CLng(b(c)) Then
Return -1
End If
End If
Else
If SortOrder = Windows.Forms.SortOrder.Ascending Or SortOrder = Windows.Forms.SortOrder.None Then
Return String.Compare(CStr(v(c)), CStr(b(c)))
Else
Return String.Compare(CStr(b(c)), CStr(v(c)))
End If
End If
End Function