PlanetSquires Forums

Support Forums => Other Software and Code => Topic started by: craigsca on December 03, 2008, 10:14:18 AM

Title: Listview - the header row
Post by: craigsca on December 03, 2008, 10:14:18 AM
I'm looking to make a Listview sortable based on which header column the user clicks on.  To do this, I'm using the LV_ITEMCHANGED event on the form and the pointer to lpNMV.iItem.  However, whenever I click on the control the .iItem only tells me which row of the control BEYOND the header I've clicked.

Is there any way to know when a user a) clicks on the header and b) which column of the header the user clicked?

Thanks!
Title: Re: Listview - the header row
Post by: TechSupport on December 03, 2008, 11:04:20 AM
Hi Craig,

You need to deal with the LVN_COLUMNCLICK notification.
Quote
Pointer to an NM_LISTVIEW structure. The iItem member is  - 1, and the iSubItem member identifies the column. All other members are zero.


Function FORM1_LISTVIEW1_LVN_COLUMNCLICK ( _
                                         ControlIndex  As Long,            _  ' index in Control Array
                                         hWndForm      As Dword,           _  ' handle of Form
                                         hWndControl   As Dword,           _  ' handle of Control
                                         ByVal lpNMV   As NM_LISTVIEW Ptr  _  ' pointer to NM_LISTVIEW
                                         ) As Long
   
   ' iSubItem contains the zero based column number that was clicked.
   MsgBox "Column:" & Str$(@lpNMV.iSubItem + 1) & " clicked."


End Function

Title: Re: Listview - the header row
Post by: TechSupport on December 03, 2008, 01:03:28 PM
Here is some code that may help you in your sorting.....


Global gSortColumn As Long

'------------------------------------------------------------------------------------------------------------------------
Function FORM1_LISTVIEW1_LVN_COLUMNCLICK ( _
                                         ControlIndex  As Long,            _  ' index in Control Array
                                         hWndForm      As Dword,           _  ' handle of Form
                                         hWndControl   As Dword,           _  ' handle of Control
                                         ByVal lpNMV   As NM_LISTVIEW Ptr  _  ' pointer to NM_LISTVIEW
                                         ) As Long
   
   ' iSubItem contains the zero based column number that was clicked.
   MsgBox "Column:" & Str$(@lpNMV.iSubItem + 1) & " clicked."

   gSortColumn = @lpNMV.iSubItem
   ListView_SortItemsEx hWndControl, CodePtr(ListView_CompareFunc), hWndControl

End Function


'------------------------------------------------------------------------------------------------------------------------
Function ListView_CompareFunc( ByVal index1 As Long, _
                               ByVal index2 As Long, _
                               ByVal hWndListView As Long _
                               )  As Long

   Local zItem1 As Asciiz * %MAX_PATH
   Local zItem2 As Asciiz * %MAX_PATH
   Local nCol   As Long
   Local nNumeric As Long
   
   ' Determine what type of sort (numeric or character) to do depending on the column clicked on.
   Select Case gSortColumn
      Case 0:  nNumeric = %TRUE   
      Case 1:  nNumeric = %FALSE   
      Case 2:  nNumeric = %FALSE   
   End Select
   
   ListView_GetItemText hWndListView, index1, nCol, zItem1, %MAX_PATH
   ListView_GetItemText hWndListView, index2, nCol, zItem2, %MAX_PATH
   
   If nNumeric Then
      If Val(zItem1) < Val(zItem2) Then
         Function = -1: Exit Function
      End If   
      If Val(zItem1) > Val(zItem2) Then
         Function = +1: Exit Function
      End If   
      If Val(zItem1) = Val(zItem2) Then
         Function = 0: Exit Function
      End If   
   Else
      zItem1 = LTrim$( zItem1 ):   zItem2 = LTrim$( zItem2 )
      Function = Lstrcmpi( zItem1, zItem2 )
   End If
     
End Function


Title: Re: Listview - the header row
Post by: craigsca on December 04, 2008, 12:14:04 AM
Is there any way to ensure that it's only the header that the user clicked?

Thanks much for the response!
Title: Re: Listview - the header row
Post by: TechSupport on December 04, 2008, 09:48:45 AM
I don't understand your question? The LVN_COLUMNCLICK notification is only fired when the user clicks on the column header so no other tests are required to determine where the user has clicked. It won't get executed if the user simply clicks on any of the rows in the ListView.
Title: Re: Listview - the header row
Post by: craigsca on December 04, 2008, 10:40:49 AM
Sorry, didn't realize that.  Looks like I have my solution then.  Thank you!
Title: Re: Listview - the header row
Post by: Jean-Pierre LEROY on June 21, 2009, 10:23:11 AM
Hi Paul,

I tried this code that you wrote last december; it doesn't seem to work for me; is it normal to pass twice the handle hWndControl ?

ListView_SortItemsEx hWndControl, CodePtr(ListView_CompareFunc), hWndControl

Jean-Pierre
Title: Re: Listview - the header row
Post by: TechSupport on June 21, 2009, 10:29:13 AM
The second hWndControl is passed as an application defined parameter. It is basically any 32-bit value that the user wants to send to the sorting callback procedure. In this case, I guess that I needed the control's window handle in the sorting callback. Here is the documentation: http://msdn.microsoft.com/en-us/library/bb775133(VS.85).aspx

Title: Re: Listview - the header row
Post by: Jean-Pierre LEROY on June 22, 2009, 06:53:41 PM
Paul,

Thank you for pointing me to the right direction.

I updated your code:
- I removed the global variable gSortColumn which is no longer necessary; now the column is passed to the sorting callback procedure.
- I completed the ListView_CompareFunc() so the type of sort (date, numeric or character) is detected automatically; see the new functions IsDate() and IsNumeric().

Last edit: June 24
- I added a new global variable gListViewSort to define the sort order.
- I added a new sub ShowHeaderIcon() to draw a draw up-arrow or down-arrow in the ListView header depending on the sort order.

Here is the code:


'--------------------------------------------------------------------------------
Function FORM1_LISTVIEW1_LVN_COLUMNCLICK ( _
                                                       ControlIndex  As Long,            _  ' index in Control Array
                                                       hWndForm      As Dword,           _  ' handle of Form
                                                       hWndControl   As Dword,           _  ' handle of Control
                                                       ByVal lpNMV   As NM_LISTVIEW Ptr  _  ' pointer to NM_LISTVIEW
                                                       ) As Long
     
    ' iSubItem contains the zero based column number that was clicked.
    If gListViewSort = %SO_ASCENDING Then gListViewSort = %SO_DESCENDING Else gListViewSort = %SO_ASCENDING
   
    ' to suppress the arrow on the previous sorted column
    If gListViewLastSortedColumn <> - 1 Then ShowHeaderIcon(HWND_FORM1_LISTVIEW1, gListViewLastSortedColumn, 0)
   
    ' sort the ListView on the selected column     
    ListView_SortItemsEx hWndControl, CodePtr(ListView_CompareFunc), @lpNMV.iSubItem
   
    ' Draws a down-arrow or up-arrow on this item.
    ShowHeaderIcon(HWND_FORM1_LISTVIEW1, @lpNMV.iSubItem, gListViewSort)
   
    ' to save the column number that has been sorted
    gListViewLastSortedColumn = @lpNMV.iSubItem
   
    ' to select the first line of the ListView
    FF_ListView_SetSelectedItem (HWND_FORM1_LISTVIEW1, 0)   
   
End Function

Function IsDate(pValue As Asciiz * %MAX_PATH) As Long
    If Verify(pValue, "0123456789/") = 0 And Instr(pValue, "/") <> 0 Then Function = %TRUE Else Function = %FALSE   
End Function

Function IsNumeric(pValue As Asciiz * %MAX_PATH) As Long
    If Verify(pValue, "0123456789.,+- ") = 0 Then Function = %TRUE Else Function = %FALSE   
End Function

'----------------------------------------------------------------------------------------------------
'* Dependence on global variables: gListViewSort (Sort Order, either %SO_ASCENDING or %SO_DESCENDING)
'----------------------------------------------------------------------------------------------------
Function ListView_CompareFunc( ByVal index1 As Long, _
                               ByVal index2 As Long, _
                               ByVal pColumn As Long _
                               )  As Long

    Local zItem1    As Asciiz * %MAX_PATH
    Local zItem2    As Asciiz * %MAX_PATH
    Local lDate1    As Asciiz * 10
    Local lDate2    As Asciiz * 10
    Local lNumeric1 As Double
    Local lNumeric2 As Double
    Local lReturn   As Long
   
    ' get the value from the ListView     
    ListView_GetItemText HWND_FORM1_LISTVIEW1, index1, pColumn, zItem1, %MAX_PATH
    ListView_GetItemText HWND_FORM1_LISTVIEW1, index2, pColumn, zItem2, %MAX_PATH
   
    '--------------------------------------------------------------------------------------------
    ' Determine what type of sort (Date, Numeric or Character) depending on the column clicked on
    '--------------------------------------------------------------------------------------------
   
    ' Date   
    If IsDate(zItem1) And IsDate(zItem2) Then
        lDate1 = DateAff10ToFile(zItem1)
        lDate2 = DateAff10ToFile(zItem2)
        lReturn = Lstrcmpi(lDate1, lDate2)   
    Else   
        ' Numeric
        If IsNumeric(zItem1) And IsNumeric(zItem2) Then
            lNumeric1 = Val(zItem1)
            lNumeric2 = Val(zItem2)         
            If lNumeric1 < lNumeric2 Then lReturn = -1 Else If lNumeric1 > lNumeric2 Then lReturn = +1 Else lReturn = 0         
        Else       
            ' Character
            zItem1 = LTrim$(zItem1)
            zItem2 = LTrim$( zItem2 )
            lReturn = Lstrcmpi(zItem1, zItem2)
        End If
       
    End If  ' If IsDate(zItem1) And IsDate(zItem2) Then
   
    If gListViewSort = %SO_ASCENDING Then Function = lReturn Else Function = -lReturn                         
       
End Function

Sub ShowHeaderIcon(hWndControl As Dword, ByVal pColumn As Long, ByVal pSortOrder As Long)

    Local hHeader As Dword
    Local HDI As HD_ITEM
   
    hHeader = SendMessage(hWndControl, %LVM_GetHeader, 0, ByVal 0)   
    HDI.mask = %HDI_FORMAT
    Header_GetItem(hHeader, pColumn, HDI)
   
    ' depending on the SortOrder (%SO_NONE, %SO_ASCENDING, %SO_DESCENDING)
    Select Case pSortOrder
   
        Case 0:
            ' remove down-arrow or up-arrow
            HDI.fmt = HDI.fmt And Not (%HDF_SORTDOWN Or %HDF_SORTUP)       
       
        Case %SO_ASCENDING:
            ' Draws an up-arrow on this item
            HDI.fmt = HDI.fmt And Not %HDF_SORTDOWN
            HDI.fmt = HDI.fmt Or      %HDF_SORTUP                       
                     
        Case %SO_DESCENDING
            'Draws a down-arrow On this item
            HDI.fmt = HDI.fmt And Not %HDF_SORTUP
            HDI.fmt = HDI.fmt Or      %HDF_SORTDOWN                             
   
    End Select
   
    Header_SetItem(hHeader, pColumn, HDI)
   
End Sub



This code works pretty well; I hope it will be helpfull to other users.

Thanks
Jean-Pierre