Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Listview Numerical Sorting 2

Status
Not open for further replies.

nevets2001uk

IS-IT--Management
Jun 26, 2002
609
GB
I am working with a ListView control and have a number of columns of data. The first sub item is a reference which is just a standard integer.

I would like to sort the ListView in decending order by this reference. I have set up the sorting but it only sorts alphabetically.

Is there a simple way to sort this column numerically?

Any help appriciated,

Steve
 
nevets2001UK
>Is there a simple way to sort this column numerically?

the search is alphabetically based - numbers are sorted as string.
ie
1
10
12
2
etc

But dates are sorted according to earliest / latest

Maybe changing the datatype of the reference to date may help (only in the listview though) or if the reference is required in view, add a hidden column to hold this "date" reference.

Alternatively, can you not present the data from your datasource pre-sorted?

Take Care

Matt
If at first you don't succeed, skydiving is not for you.
 
BTW
take a look at thread222-570674 if you are using listviews. There are some really useful tips for resizing columns

Take Care

Matt
If at first you don't succeed, skydiving is not for you.
 
yep, the Listview sorts alphabetically. It is one of it's limitations. However, a brief visit to the API allows us to define our own sorting function for a Listview. I presented a version of this in thread222-569889, but actually managed to post the wrong code there (an original example from MVPS, which is buggy, rather than my own revised version). So here's the revision. You'll need a form with two command button and a listview. You will also need a module.

The following code goes in the form:
[tt]
Option Explicit

Private Sub Command1_Click()
Dim lp As Long

' Get LV loaded with numbers 0 to 100 alpha sorted
ListView1.Sorted = True
For lp = 0 To 100
ListView1.ListItems.Add , , CStr(lp)
Next

End Sub

Private Sub Command2_Click()
ListView1.Sorted = False
' Repeat sort, since the sort will not be complete on first pass if
' source is very mixed up
Do
AllSorted = True
SendMessage ListView1.hWnd, LVM_SORTITEMS, ListView1.hWnd, ByVal FARPROC(AddressOf CompareValues)
Loop Until AllSorted
End Sub
[/tt]
And the following code goes in the module:
[tt]
Option Explicit

Public objFind As LV_FINDINFO
Public objItem As LV_ITEM

Public Type POINTAPI
x As Long
y As Long
End Type

Public Type LV_FINDINFO
flags As Long
psz As String
lParam As Long
pt As POINTAPI
vkDirection As Long
End Type

Public Type LV_ITEM
mask As Long
iItem As Long
iSubItem As Long
state As Long
stateMask As Long
pszText As String
cchTextMax As Long
iImage As Long
lParam As Long
iIndent As Long
End Type

'Constants
Public Const LVFI_PARAM As Long = &H1
Public Const LVIF_TEXT As Long = &H1

Public Const LVM_FIRST As Long = &H1000
Public Const LVM_FINDITEM As Long = (LVM_FIRST + 13)
Public Const LVM_GETITEMTEXT As Long = (LVM_FIRST + 45)
Public Const LVM_SORTITEMS As Long = (LVM_FIRST + 48)

'API declarations
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public AllSorted As Boolean

Public Function CompareValues(ByVal lParam1 As Long, ByVal lParam2 As Long, ByVal hWnd As Long) As Long

'CompareValues: This is the sorting routine that gets passed to the
'ListView control to provide the comparison test for numeric values.

'Compare returns:
' -1 = Less Than
' 0 = Equal
' 1 = Greater Than

Dim val1 As Long
Dim val2 As Long

'Obtain the item names and values corresponding
'to the input parameters
val1 = ListView_GetItemValueStr(hWnd, lParam1)
val2 = ListView_GetItemValueStr(hWnd, lParam2)

' 'sort descending
' If val1 > val2 Then
' CompareValues = -1
' ElseIf val1 = val2 Then
' CompareValues = 0
' Else
' CompareValues = 1
' AllSorted = False
' End If

' sort ascending
If val2 > val1 Then
CompareValues = -1

ElseIf val1 = val2 Then
CompareValues = 0
Else
CompareValues = 1
AllSorted = False
End If

End Function

Public Function ListView_GetItemValueStr(hWnd As Long, lParam As Long) As Long

Dim hIndex As Long
Dim r As Long

'Convert the input parameter to an index in the list view
objFind.flags = LVFI_PARAM
objFind.lParam = lParam
hIndex = SendMessage(hWnd, LVM_FINDITEM, -1, objFind)

'Obtain the value of the specified list view item.
'The objItem.iSubItem member is set to the index
'of the column that is being retrieved.
objItem.mask = LVIF_TEXT
objItem.iSubItem = 0 ' Change this if you want to use a different subitem as the sort key. More-or-less equivalent to the ListView's SortKey property
objItem.pszText = Space$(32)
objItem.cchTextMax = Len(objItem.pszText)

r = SendMessage(hWnd, LVM_GETITEMTEXT, hIndex, objItem)
If r > 0 Then
ListView_GetItemValueStr = CLng(Left$(objItem.pszText, r))
End If
'Beep
End Function

Public Function FARPROC(ByVal pfn As Long) As Long

'A procedure that receives and returns
'the value of the AddressOf operator.
'This workaround is needed as you can't assign
'AddressOf directly to an API when you are also
'passing the value ByVal in the statement
'(as is being done with SendMessage)

FARPROC = pfn

End Function
 
Yes strongm, these "custom" methods used for sorting listviews are really useful, as they can be used to sort listviews using numbers, dates or any other user-defined criteria.

However, there is another method for sorting listviews by numbers in my mind. Although it is not as robust and professional as strongm's method, the main purpose of my post is to share the idea - not to propose it as a solution as I would also recommend strongm's method.

With a (version 6) listview on your form (ListView1), add the following code and run the program to test numerical sorting...
___
Code:
Option Explicit

Private Sub Form_Load()
    Dim N As Integer
    With ListView1
        .View = lvwReport
        .LabelEdit = lvwManual
        'add sample columns
        .ColumnHeaders.Add , , "Number", 900
        .ColumnHeaders.Add , , "Font", 3000
        'add sample items
        For N = 1 To Screen.FontCount - 1
            .ListItems.Add(, , N).SubItems(1) = Screen.Fonts(N)
        Next
    End With
End Sub

Private Sub Form_Resize()
    ListView1.Move 0, 0, ScaleWidth, ScaleHeight
End Sub

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As ColumnHeader)
    With ListView1
        .Sorted = False
        If .SortKey <> ColumnHeader.Index - 1 Then
            .SortKey = ColumnHeader.Index - 1
        Else
            .SortOrder = 1 - .SortOrder
        End If
        If ColumnHeader.Index = 1 Then
            SortNumeric
        Else
            .Sorted = True
        End If
    End With
End Sub

Sub SortNumeric()
    Dim S As String * 10, N As Integer
    With ListView1
        'justify the text using padding spaces
        For N = 1 To .ListItems.Count
            S = vbNullString
            If .SortKey Then
                RSet S = .ListItems(N).SubItems(.SortKey)
                .ListItems(N).SubItems(.SortKey) = S
            Else
                RSet S = .ListItems(N)
                .ListItems(N).Text = S
            End If
        Next
        'sort column using &quot;justified&quot; text
        .Sorted = True
        'trim spaces from the text
        For N = 1 To .ListItems.Count
            If .SortKey Then
                .ListItems(N).SubItems(.SortKey) = _
                LTrim$(.ListItems(N).SubItems(.SortKey))
            Else
                .ListItems(N).Text = LTrim$(.ListItems(N))
            End If
        Next
    End With
End Sub
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top