Sorting Multicolumn Listview
BlitzMax Forums/MaxGUI Module/Sorting Multicolumn Listview
| ||
| I tried to modify the code by Zitch (some changes done by Cardonic) to make a Listview sortable. An easy way to do (but like it seems also to difficult for me :) ) I found in a VB example: http://www.microsoft.com/germany/msdn/library/visualtools/vb6/ListViewZeilenInVisualBasicKorrektSortieren.mspx?mfr=true The code itself works and if you rem out the SortListView after the event occurs anything is fine. Only the sort routines itself are a little bit buggy ... The program crashes here: Return CallWindowProc(oldListProc, hWnd, MSG, wParam, Int(Byte Ptr(lParam))) : Unhandled Memory Exception Error but like I said, only when SortListView is called. I know that the code is ugly (I mean my additional one no offence to the code I just copied) but this will be changed when there's a working example.
Strict
Extern "win32"
Function SendMessage:Int(hWnd:Int,MSG:Int,wParam:Int,lParam:Int) = "SendMessageA@16"
Function SetWindowLong:Int(hWnd:Int,nIndex:Int,lNewLong:Int) = "SetWindowLongA@12"
Function GetWindowLong:Int(hWnd:Int, nIndex:Int) = "GetWindowLongA@8"
Function CallWindowProc:Int(lpPrevWndFunc, hWnd, uMsg, wParam, lParam) = "CallWindowProcA@20"
End Extern
Const LBS_NOTIFY:Int = $1
Const LBS_SORT:Int = $2
Const LBS_NOREDRAW:Int = $4
Const LBS_MULTIPLESEL:Int = $8
Const LBS_OWNERDRAWFIXED:Int = $10
Const LBS_OWNERDRAWVARIABLE:Int = $20
Const LBS_HASSTRINGS:Int = $40
Const LBS_USETABSTOPS:Int = $70
Const LBS_NOINTEGRALHEIGHT:Int = $100
Const LBS_MULTICOLUMN:Int = $200
Const LBS_WANTKEYBOARDINPUT:Int = $400
Const LBS_EXTENDEDSEL:Int = $800
Const LBS_DISABLENOSCROLL:Int = $100
Const LBS_NODATA:Int = $2000
Const LBS_NOSEL:Int = $4000
Const LBS_STANDARD:Int = $a00003
Const LVM_FIRST:Int = $1000
Const LVM_GETITEMA:Int = (LVM_FIRST+5)
Const LVM_SETITEMA:Int = (LVM_FIRST+6)
Const LVM_INSERTITEMA:Int = (LVM_FIRST+7)
Const LVM_DELETEITEM:Int = (LVM_FIRST+8)
Const LVM_DELETEALLITEMS:Int = (LVM_FIRST+9)
Const LVM_GETCALLBACKMASK:Int = (LVM_FIRST+10)
Const LVM_SETCALLBACKMASK:Int = (LVM_FIRST+11)
Const LVM_GETCOLUMNA:Int = (LVM_FIRST+25)
Const LVM_SETCOLUMNA:Int = (LVM_FIRST+26)
Const LVM_INSERTCOLUMNA:Int = (LVM_FIRST+27)
Const LVM_DELETECOLUMN:Int = (LVM_FIRST+28)
Const LVM_GETCOLUMNWIDTH:Int = (LVM_FIRST+29)
Const LVM_SETCOLUMNWIDTH:Int = (LVM_FIRST+30)
Const LVM_GETTEXTCOLOR:Int = (LVM_FIRST+35)
Const LVM_SETTEXTCOLOR:Int = (LVM_FIRST+36)
Const LVM_GETTEXTBKCOLOR:Int = (LVM_FIRST+37)
Const LVM_SETTEXTBKCOLOR:Int = (LVM_FIRST+38)
Const LVM_SORTITEMS:Int = (LVM_FIRST+48)
Const LVM_SETLISTBOXEXSTYLE:Int = (LVM_FIRST+54)
Const LVM_GETITEMW:Int = (LVM_FIRST+75)
Const LVM_SETITEMW:Int = (LVM_FIRST+76)
Const LVM_INSERTITEMW:Int = (LVM_FIRST+77)
Const LVM_GETCOLUMNW:Int = (LVM_FIRST+95)
Const LVM_SETCOLUMNW:Int = (LVM_FIRST+96)
Const LVM_INSERTCOLUMNW:Int = (LVM_FIRST+97)
Const LVS_EX_GRIDLINES:Int = $1
Const LVS_EX_SUBITEMIMAGES:Int = $2
Const LVS_EX_CHECKBOXES:Int = $4
Const LVS_EX_TRACKSELECT:Int = $8
Const LVS_EX_HEADERDRAGDROP:Int = $10
Const LVS_EX_FULLROWSELECT:Int = $20
Const LVS_EX_ONECLICKACTIVATE:Int = $40
Const LVS_EX_TWOCLICKACTIVATE:Int = $80
Const LVS_EX_FLATSB:Int = $100
Const LVS_EX_REGIONAL:Int = $200
Const LVS_EX_INFOTIP:Int = $400
Const LVS_EX_UNDERLINEHOT:Int = $800
Const LVS_EX_UNDERLINECOLD:Int = $1000
Const LVS_EX_MULTIWORKAREAS:Int = $2000
Const LVS_EX_LABELTIP:Int = $4000
Const LVS_EX_BORDERSELECT:Int = $8000
Const LVS_EX_DOUBLEBUFFER:Int = $10000
Const LVS_EX_HIDELABELS:Int = $20000
Const LVS_EX_SINGLEROW:Int = $40000
Const LVS_EX_SNAPTOGRID:Int = $80000
Const LVS_EX_SIMPLESELECT:Int = $100000
Const LVCF_FMT:Int = $1
Const LVCF_WIDTH:Int = $2
Const LVCF_TEXT:Int = $4
Const LVCF_SUBITEM:Int = $8
Const LVCF_IMAGE:Int = $10
Const LVCF_ORDER:Int = $20
Const LVIF_TEXT:Int = $1
Const LVIF_IMAGE:Int = $2
Const LVIF_PARAM:Int = $4
Const LVIF_STATE:Int = $8
Const LVIF_INDENT:Int = $10
Const LVIF_GROUPID:Int = $100
Const LVIF_COLUMNS:Int = $200
Const LVIF_NORECOMPUTE:Int = $800
Const LVIF_DI_SETITEM:Int = $1000
Const LVS_ALIGNTOP:Int = $0
Const LVS_ICON:Int = $0
Const LVS_REPORT:Int = $1
Const LVS_SMALLICON:Int = $2
Const LVS_LIST:Int = $3
Const LVS_TYPEMASK:Int = $3
Const LVS_SINGLESEL:Int = $4
Const LVS_SHOWSELALWAYS:Int = $8
Const LVS_SORTASCENDING:Int = $10
Const LVS_SORTDESCENDING:Int = $20
Const LVS_SHAREIMAGELISTS:Int = $40
Const LVS_NOLABELWRAP:Int = $80
Const LVS_AUTOARRANGE:Int = $100
Const LVS_EDITLABELS:Int = $200
Const LVS_OWNERDRAWFIXED:Int = $400
Const LVS_ALIGNLEFT:Int = $800
Const LVS_ALIGNMASK:Int = $C00
Const LVS_NOSCROLL:Int = $2000
Const LVS_NOCOLUMNHEADER:Int = $4000
Const LVS_NOSORTHEADER:Int = $8000
Const LVS_TYPESTYLEMASK:Int = $FC00
Const GWL_STYLE:Int = -$10
Const HDN_ITEMCLICK:Int = -302
Const GWL_WNDPROC:Int = -4
Const WM_NOTIFY = 78
Const HDN_ITEMCLICKW = -322
Type LVCOLUMN
Field mask:Int
Field fmt:Int
Field cx:Int
Field pszText:Byte Ptr
Field cchTextMax:Int
Field iSubItem:Int
EndType
Type LVITEM
Field mask:Int
Field iItem:Int
Field iSubItem:Int
Field iState:Int
Field stateMask:Int
Field pszText:Byte Ptr
Field cchTextMax:Int
Field iImage:Int
Field lParam:Byte Ptr
Field iIndent:Int
Field iGroupId:Int
Field cColumns:Int
Field puColumns:Int
EndType
Type HD_NOTIFY
'Field hdr:Byte Ptr ' NMHDR
Field hwndFrom
Field idFrom
Field code
Field iItem
Field iButton
Field pitem
EndType
Type LVWSORT
Field hWndListView:Int ' Fensterhandle des ListView-Controls
Field SortKey:Int ' Spalte, die sortiert werden soll
Field SortType:Int ' Typ der zu sortierenden Daten
Field SortOrder:Int ' Sortierrichtung
End Type
Const lvwAscending:Int = 0 ' Aufsteigende Sortierung
Const lvwDescending:Int = 1 ' Absteigende Sortierung
Const stString:Int = 1 ' Stringsortierung
Const stDate:Int = 2 ' Datensortierung
Const stNumeric:Int = 3 ' Zahlensortierung
Const LVM_FINDITEM:Int = (LVM_FIRST + 13)
Const LVFI_PARAM:Int = $1
Type POINTAPI ' ben�tigt f�r LV_FINDINFO
Field x:Int
Field y:Int
End Type
Type LV_FINDINFO
Field flags:Int
Field psz:String
Field lParam:Int
Field pt:POINTAPI
Field vkDirection:Long
End Type
Const LVM_GETITEMTEXT:Int = (LVM_FIRST + 45)
Function AddListBoxColumn(ListBox:TGadget, ColumnNr:Int, Width:Int)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local Column:LVColumn = New LVColumn
Column.mask = LVCF_WIDTH
Column.cx = Width
SendMessageA(ListBoxHwnd,LVM_INSERTCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function
Function SetListBoxHeading(ListBox:TGadget,ColumnNr:Int,HeadingText:String,Style:Int=0)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local Column:LVColumn = New LVColumn
Column.mask = LVCF_TEXT | LVCF_FMT
Column.fmt = Style
Column.pszText = HeadingText.ToCString()
Local ListBoxStyle:Int = GetWindowLongA(ListBoxHwnd,GWL_STYLE)
If (ListBoxStyle & LVS_NOCOLUMNHEADER ) Then
ListBoxStyle = ListBoxStyle ~ LVS_NOCOLUMNHEADER
SetWindowLongA(ListBoxHwnd,GWL_STYLE,ListBoxStyle)
EndIf
SendMessageA(ListBoxHwnd,LVM_SETCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function
Function SetListBoxColumnWidth(ListBox:TGadget,ColumnNr:Int,Width:Int)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local Column:LVColumn = New LVColumn
Column.mask = LVCF_WIDTH
Column.cx = Width
SendMessageA(ListBoxHwnd,LVM_SETCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function
Function ModifyListBoxItem(ListBox:TGadget,RowNr:Int,ColumnNr:Int,Text:String)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local Item:LVItem = New LVItem
Item.mask = LVIF_TEXT
Item.iSubItem = ColumnNr
Item.iItem = RowNr
Item.pszText = Text.ToCString()
Item.cchTextMax = Text.Length+1
SendMessageA(ListBoxHwnd,LVM_SETITEMA,0,Int(Byte Ptr Item))
End Function
Function GetListBoxItemText:String(ListBox:TGadget,RowNr:Int,ColumnNr:Int)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local ReturnText:String
Local TextBank:TBank = CreateBank(1024)
Local Item:LVItem = New LVItem
Item.mask = LVIF_Text
Item.iSubItem = ColumnNr
Item.iItem = RowNr
Item.pszText = BankBuf(TextBank)
Item.cchTextMax = 255
SendMessageA(ListBoxHwnd,LVM_GETITEMA,0,Int(Byte Ptr Item))
If Item.pszText <> Null Then
ReturnText = ReturnText.FromCString(Item.pszText)
EndIf
Return ReturnText
End Function
Function SetListBoxExtendedStyle(ListBox:TGadget,Style:Int)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
SendMessageA(ListBoxHwnd,LVM_SETLISTBOXEXSTYLE,Style,Style)
End Function
Function SetListBoxMultiSelect(ListBox:TGadget, Enable:Byte = True)
Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
Local ListBoxStyle:Int = GetWindowLongA(ListboxHwnd,GWL_STYLE)
If Sgn(ListBoxStyle & LVS_SINGLESEL) = Enable Then
ListBoxStyle = ListBoxStyle ~ LVS_SINGLESEL
EndIf
SetWindowLongA(ListBoxHwnd,GWL_STYLE,ListBoxStyle)
End Function
Function NewListProc:Int(hWnd:Int, Msg:Int, wParam:Int, lParam:Int) "win32"
If MSG = WM_NOTIFY Then
Local NotifyMess:HD_NOTIFY = New HD_NOTIFY
MemCopy(Byte Ptr(NotifyMess), Byte Ptr(lParam), SizeOf(HD_NOTIFY))
'DebugLog "hwndFrom " + NotifyMess.hwndFrom
'DebugLog "idFrom " + NotifyMess.idFrom
'DebugLog "code " + NotifyMess.code
'DebugLog "iItem " + NotifyMess.iitem
'DebugLog "iButton " + NotifyMess.iButton
'DebugLog "pitem " + NotifyMess.pitem
If NotifyMess.code = HDN_ITEMCLICKW Then
Local event:TEvent = New TEvent
event.id = HDN_ITEMCLICKW
event.source = ActiveGadget()
event.data = NotifyMess.iitem
PostEvent(event)
'DebugLog(NotifyMess.iitem)
EndIf
EndIf
If oldListProc <> 0 Then
DebugLog "MSG: " + MSG
DebugLog "hWnd: " + hwnd
DebugLog "wParam: " + wParam
DebugLog "lParam: " + lParam
Return CallWindowProc(oldListProc, hWnd, MSG, wParam, Int(Byte Ptr(lParam)))
EndIf
EndFunction
Function SortListView:Int(ListBox:TGadget, SortKey:Int, SortType:Int = stString, SortOrder:Int = lvwAscending)
' -----------------------------------------------------
' �ffentlich aufzurufende Prozedur SortListView, die
' f�r die individuelle Sortierung einer ListView-Spalte
' sorgt.
' -----------------------------------------------------
' hWndListView: Fensterhandle des ListView-Steuerelements
' SortKey: Spalte (nullbasiert), die sortiert werden
' soll (= Spaltennummer - 1).
' SortType: stString, um Strings zu sortieren (Standardwert)
' stDate, um Datumsangaben zu sortieren
' stNumeric, um Zahlen zu sortieren
' SortOrder: lvwAscending f�r aufsteigende Sortierung (Std.)
' lvwDescending f�r absteigende Sortierung
' -----------------------------------------------------
' �bergebene Informationen in einer LVWSORT-
' Struktur zusammenfassen:
Local udtLVWSORT:LVWSORT = New LVWSORT
udtLVWSORT.hWndListView = QueryGadget(ListBox, QUERY_HWND)
udtLVWSORT.SortKey = SortKey
udtLVWSORT.SortOrder = SortOrder
udtLVWSORT.SortType = SortType
' Eigene Sortierfunktionalit�t in der Funktion
' CompareFunc verwenden: Die Informationen der
' LVWSORT-Struktur wird mithilfe eines Zeigers
' auf die Variable udtLVWSORT beigegeben:
SendMessage QueryGadget(ListBox, QUERY_HWND), LVM_SORTITEMS, Int(Byte Ptr(udtLVWSORT)), Int(Byte ptr(CompareFunc))
End Function
Function CompareFunc:Int(lParam1:Int, lParam2:Int, lParamSort:Int)
' -----------------------------------------------------
' Vergleichsfunktion CompareFunc
' -----------------------------------------------------
' Verglichen werden jeweils zwei Elemente der zu
' sortierenden Spalte des ListView-Steuerelements,
' die �ber lParam1 und lParam2 angegeben werden.
' Hierbei wird �ber den R�ckgabewert der Funktion
' bestimmt, welches der beiden Elemente als gr��er
' gelten soll (hier f�r Aufw�rtssortierung):
' * Element 1 < Element 2: R�ckgabewert < 0
' * Element 1 = Element 2: R�ckgabewert = 0
' * Element 1 > Element 2: R�ckgabewert > 0
' -----------------------------------------------------
Local ListViewSort:LVWSORT = New LVWSORT
Local sEntry1:String
Local sEntry2:String
Local vCompare1:VARIANT
Local vCompare2:VARIANT
' In lParamSort von SortListView als Long-Pointer
' �bergebene LVWSORT-Struktur abholen, um auf deren
' Werte zugreifen zu k�nnen:
MemCopy(Byte Ptr(ListViewSort), Byte Ptr(lParamSort), SizeOf(ListViewSort))
' Die Werte der zu vergleichenden Elemente werden
' mithilfe der privaten Funktion LvwGetText aus
' den Angaben lParam1 und lParam2 ermittelt:
sEntry1 = LvwGetText(ListViewSort, lParam1)
sEntry2 = LvwGetText(ListViewSort, lParam2)
DebugLog sEntry1 + " " + sEntry2
' Sind die Elemente gleich, kann die Funktion
' sofort mit dem aktuellen R�ckgabewert 0
' verlassen werden:
If sEntry1 = sEntry2 Then
Return
End If
' F�r die Sortierung wird unterschieden zwischen
' Daten, Zahlen und allgemeinen Strings. Hierf�r
' steht jeweils eine separate, private Vergleichs-
' funktion zur Verf�gung.
Select ListViewSort.SortType
Case stDate ' Spalteninhalte sind Datumswerte
'CompareFunc = CompareDates(sEntry1, sEntry2, ListViewSort.SortOrder)
Case stNumeric ' Spalteninhalte sind Zahlen
'CompareFunc = CompareNumbers(sEntry1, sEntry2, ListViewSort.SortOrder)
Case stString ' Spalteninhalte sind Strings
Local CompareFunc:Int = CompareStrings(sEntry1, sEntry2, ListViewSort.SortOrder)
End Select
End Function
Function LvwGetText:String(ListViewSort:LVWSORT, lParam:Int)
' -----------------------------------------------------
' Ermittelt aus dem Fensterhandle des ListView-
' Steuerelements, der in ListViewSort.SortKey
' angegebenen (nullbasierten) Spalte im ListView
' und der an CompareFunc �bergebenen Werte lParam1/2
' die davon repr�sentierten Zelleninhalte.
' -----------------------------------------------------
Local udtFindInfo:LV_FINDINFO = New LV_FINDINFO
Local udtLVItem:LVITEM = New LVITEM
Local lngIndex:Int
'Local baBuffer:Byte[512]
Local TextBank:TBank = CreateBank(1024)
Local lngLength:Int
Local ReturnText:String
' Den Index der Zelle aus lParam ermitteln:
udtFindInfo.flags = LVFI_PARAM
udtFindInfo.lParam = lParam
lngIndex = SendMessage(ListViewSort.hWndListView, LVM_FINDITEM, - 1, Int(Byte Ptr(udtFindInfo)))
' Auf Basis des gefundenen Index den Text der Zelle
' in ein Byte-Array �bertragen:
udtLVItem.mask = LVIF_TEXT
udtLVItem.iSubItem = ListViewSort.SortKey
udtLVItem.pszText = BankBuf(TextBank)
'Int(VarPtr(baBuffer[0] ))
udtLVItem.cchTextMax = 255
'lngLength = SendMessage(ListViewSort.hWndListView, LVM_GETITEMTEXT, lngIndex, Int(Byte Ptr(udtLVItem)))
' Byte-Array in passender L�nge als String-
' R�ckgabewert kopieren:
If lngLength > 0 Then
ReturnText = ReturnText.FromCString(udtLVItem.pszText)
DebugLog lngLength + " " + ReturnText
Return ReturnText
'LvwGetText = Left:String(StrConv(baBuffer, vbUnicode), lngLength)
End If
End Function
Function CompareStrings:Int(sEntry1:String, sEntry2:String, SortOrder:Int)
' -----------------------------------------------------
' Gibt zurück, ob das erste der beiden unterschiedlichen
' Elemente nach Maßgabe des Parameters SortOrder größer
' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
' aufsteigender Sortierung) als das zweite Element ist.
' Gleiche Elemente wurden bereits in CompareFunc ausge-
' schlossen; für sie wäre sonst 0 zurückzugeben.
' -----------------------------------------------------
' Rückgabewert je nach erwünschter Sortierung:
If SortOrder = lvwAscending Then
' Aufsteigende Sortierung zweier unterschiedlicher Strings
If sEntry1 < sEntry2 Then
Return - 1
Else
Return 1
End If
Else ' Absteigende Sortierung
If sEntry1 > sEntry2 Then
Return - 1
Else
Return 1
End If
End If
End Function
Local Window:TGadget = CreateWindow("Erweiterte ListBox f�r BMax", 100, 100, 500, 300, Null, 1)
Local ListBox:TGadget = CreateListBox(2,2,ClientWidth(Window)-4,ClientHeight(Window)-4,Window)
Local ListBoxExtendedStyle:Int = LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES '| LVS_EX_FLATSB
'SetListBoxExtendedStyle(ListBox,ListBoxExtendedStyle)
AddListBoxColumn(ListBox,1,100)
AddListBoxColumn(ListBox,2,100)
AddListBoxColumn(ListBox,3,100)
AddListBoxColumn(ListBox,4,100)
SetListBoxHeading(ListBox,1,"T")
SetListBoxHeading(ListBox,2,"E")
SetListBoxHeading(ListBox,3,"S")
SetListBoxHeading(ListBox,4,"T")
Global oldListProc
oldListProc = SetWindowLongA(QueryGadget(ListBox, QUERY_HWND), GWL_WNDPROC, Int(Byte Ptr(NewListProc)))
For Local Row:Int = 0 To 29
AddGadgetItem(ListBox,Row)
For Local Column:Int = 1 To 4
ModifyListBoxItem(ListBox, Row, Column, "Item " + Row + "/" + Column)
Next
Next
ModifyListBoxItem(ListBox,10,0,"blubb")
RemoveGadgetItem(ListBox,5)
Local SelectedItem:Int
Repeat
Select WaitEvent()
Case HDN_ITEMCLICKW
SortListView ListBox, EventData()
'DebugLog "Row " + EventData() + " had been clicked"
Case EVENT_GADGETACTION
SelectedItem = SelectedGadgetItem(ListBox)
Print "SelectedItem "+SelectedItem
Print GetListBoxItemText(ListBox,SelectedItem,0)
Print GetListBoxItemText(ListBox,SelectedItem,1)
Print GetListBoxItemText(ListBox,SelectedItem,2)
Print GetListBoxItemText(ListBox,SelectedItem,3)
Print GetListBoxItemText(ListBox,SelectedItem,4)
Case EVENT_WINDOWCLOSE
End
End Select
Forever
Thanks Andre |
| ||
| This look like you are trying to use Microsofts listview sorting. I had this going a few versions of maxgui ago, but a maxgui change to get tooltips working used the lparam field in the listview structure. As lparam is used for sorting, MS sort routines no longer work. this is briefly talked about here. http://www.blitzbasic.com/Community/posts.php?topic=61318#686941 |
| ||
| Thanks Ziltch - as you wrote you'd allready a working version of sorting? Ok I understand that the tooltip make it broken and the last post in the topic was about a fix... This was about one year ago - any news about? |