I did a search here but couldn't find an existing implementation. There are several out there in the wild, dating back to an old Randy Birch example base on an even older MS KB item on sorting by date columns. So clearly people have been doing this since the early VB5 days, 1997-1998 or so.
ListViewCustomSort .cls and its companion ListViewCustomSortStatic.bas attempt to do the same thing written from scratch based on the Windows SDK. Since I wanted to make something more readily reusable it made sense to start writing anew based on the Common Controls ListView documentation.
This should be quicker with a better user experience than other techniques, for example creating extra "hidden" (width = 0) columns to sort on. However that's a perfectly viable solution as well. It just eats more memory.
What it Does
ListViewCustomSort works with both ActiveX ListView controls, the one in COMCTL32.OCX and the one in MSCOMCTL.OCX.
While you can use the existing sorting properies exposed by both ListViews for simple text sorts in ascending or descending order, there isn't a "custom sort" option and Compare Event like that provided by other controls such as the MSHFlexGrid.
ListViewCustomSort works in parallel with a ListView to provide such an event for custom sorting. While this version only handles single-column sorts you could enhance it to provide more than one column's text to the Compare Event handler.
In your Compare Event handler you get the column text of two rows to compare and you return a comparison result. In order to do something like a date or numeric compare you must compare ItemText1 and ItemText2 returning a numeric result:
This usually requires some data conversion and conditional logic.
Using ListViewCustomSort
You must add ListViewCustomSortStatic.bas and ListViewCustomSort.cls to your project that uses ListViews. ListViewCustomSortStatic.bas is needed in order to implement the required API callback function.
Then in Forms with ListView controls where you want to do custom sorting, you add a separate instance of the ListViewCustomSort Class WithEvents in order to get a custom-sort Compare Event for each ListView. If your ListViews have identical sets of columns you can get by with a single ListViewCustomSort instance.
Then you write your Compare Event handler(s) to perform the custom sort comparisons.
To perform the custom sort you set ListViewCustomSort's .Ascending (Boolean) and .SortKey (Long: ListView column index, base 0) properties, and optionally its .TextMax (Long: max text width, default 256). Then you call the .Sort() method passing the ListView control reference.
The Demo
ListViewCustomSort is included in the attached archive which includes a self-contained demonstration project. This demo creates three columns of random data: a String, a Date, and a Single column.
ListViewCustomSort isn't need for the first column's data, but it is used for the other two which require some extra effort to make sorting work properly.
![Name: sshot.png
Views: 78
Size: 36.4 KB]()
The source code of Form1.frm may help make usage a little clearer:
ListViewCustomSort .cls and its companion ListViewCustomSortStatic.bas attempt to do the same thing written from scratch based on the Windows SDK. Since I wanted to make something more readily reusable it made sense to start writing anew based on the Common Controls ListView documentation.
This should be quicker with a better user experience than other techniques, for example creating extra "hidden" (width = 0) columns to sort on. However that's a perfectly viable solution as well. It just eats more memory.
What it Does
ListViewCustomSort works with both ActiveX ListView controls, the one in COMCTL32.OCX and the one in MSCOMCTL.OCX.
While you can use the existing sorting properies exposed by both ListViews for simple text sorts in ascending or descending order, there isn't a "custom sort" option and Compare Event like that provided by other controls such as the MSHFlexGrid.
ListViewCustomSort works in parallel with a ListView to provide such an event for custom sorting. While this version only handles single-column sorts you could enhance it to provide more than one column's text to the Compare Event handler.
In your Compare Event handler you get the column text of two rows to compare and you return a comparison result. In order to do something like a date or numeric compare you must compare ItemText1 and ItemText2 returning a numeric result:
Code:
'Return values:
'
' -1 = Less Than
' 0 = Equal
' 1 = Greater Than
Using ListViewCustomSort
You must add ListViewCustomSortStatic.bas and ListViewCustomSort.cls to your project that uses ListViews. ListViewCustomSortStatic.bas is needed in order to implement the required API callback function.
Then in Forms with ListView controls where you want to do custom sorting, you add a separate instance of the ListViewCustomSort Class WithEvents in order to get a custom-sort Compare Event for each ListView. If your ListViews have identical sets of columns you can get by with a single ListViewCustomSort instance.
Then you write your Compare Event handler(s) to perform the custom sort comparisons.
To perform the custom sort you set ListViewCustomSort's .Ascending (Boolean) and .SortKey (Long: ListView column index, base 0) properties, and optionally its .TextMax (Long: max text width, default 256). Then you call the .Sort() method passing the ListView control reference.
The Demo
ListViewCustomSort is included in the attached archive which includes a self-contained demonstration project. This demo creates three columns of random data: a String, a Date, and a Single column.
ListViewCustomSort isn't need for the first column's data, but it is used for the other two which require some extra effort to make sorting work properly.
The source code of Form1.frm may help make usage a little clearer:
Code:
Option Explicit
Private WithEvents ListViewCustomSort As ListViewCustomSort
Private Function MakeName() As String
Const LETTERS As String = "abcdefghijklmnopqrstuvwxyz"
Dim I As Long
Dim IMax As Long
Dim J As Long
Dim Words() As String
IMax = Int(3 * Rnd()) + 1
ReDim Words(IMax)
For I = 0 To IMax
For J = 1 To Int(8 * Rnd()) + 2
Words(I) = Words(I) & Mid$(LETTERS, Int(26 * Rnd()) + 1, 1)
Next
Words(I) = StrConv(Words(I), vbProperCase)
Next
MakeName = Join$(Words, " ")
End Function
Private Sub cmdSortBy_Click(Index As Integer)
Select Case Index
Case 0
With ListView1
.SortKey = 0
.SortOrder = IIf(chkAscending.Value = vbChecked, lvwAscending, lvwDescending)
.Sorted = True
End With
Case 1, 2
ListViewCustomSort.Sort ListView1, Index, chkAscending.Value = vbChecked
End Select
End Sub
Private Sub Form_Load()
Dim I As Long
Randomize
Set ListViewCustomSort = New ListViewCustomSort
With ListView1.ColumnHeaders
.Add , , "Col 0", 3600, lvwColumnLeft
.Add , , "Col 1", 1080, lvwColumnRight
.Add , , "Col 2", 1080, lvwColumnRight
End With
With ListView1
With .ListItems
For I = 1 To 1000
With .Add(, , MakeName()) 'String.
.SubItems(1) = Format$(DateAdd("d", -Int(1000 * Rnd()), Date), "m/d/yyyy") 'Date.
.SubItems(2) = CStr(20000! * Rnd() - 10000!) 'Single.
End With
Next
End With
Set .SelectedItem = Nothing
End With
End Sub
Private Sub Form_Resize()
If WindowState <> vbMinimized Then
With Picture1
.Move 0, ScaleHeight - .Height
ListView1.Move 0, 0, ScaleWidth, .Top
End With
End If
End Sub
Private Sub ListViewCustomSort_Compare( _
ByVal SortKey As Long, _
ByVal Ascending As Boolean, _
ItemText1 As String, _
ItemText2 As String, _
Cmp As Long)
Dim Parts() As String
Dim Date1 As Date
Dim Date2 As Date
Dim Single1 As Single
Dim Single2 As Single
Select Case SortKey
Case 1
Parts = Split(ItemText1, "/")
Date1 = DateSerial(CInt(Parts(2)), CInt(Parts(0)), CInt(Parts(1)))
Parts = Split(ItemText2, "/")
Date2 = DateSerial(CInt(Parts(2)), CInt(Parts(0)), CInt(Parts(1)))
Cmp = Sgn(CLng(Date1) - CLng(Date2))
Case 2
Single1 = CSng(ItemText1)
Single2 = CSng(ItemText2)
Cmp = Sgn(Single1 - Single2)
End Select
If Not Ascending Then Cmp = -Cmp
End Sub