Results 1 to 1 of 1

Thread: [VB6] ListViewCustomSort Helper Class

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Smile [VB6] ListViewCustomSort Helper Class

    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:

    Code:
    'Return values:
    '
    '   -1 = Less Than
    '    0 = Equal
    '    1 = Greater Than
    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: 1146
Size:  36.4 KB

    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
    Attached Files Attached Files

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width