Im dont use list views much, and so its not something I know alot about. Im filling a listview with drive file info, and i would like to sort it afterwards. For somereason beyond me, the LVS_SORTASCENDING only works on the REPORT listview if there is only one column of information. The moment i start adding a second it goes all wierd and shows about 1 in 5 entries. The rest are blanked.

If it turn off the style, all columns fill as the were programmed to.

So after searchin the boad, i only found IcZelion's example, but I havent gotten it to work properly (using a callback to sort). For some reason, each callback returns the same pointer.

If someone has a simple example of how to sort a list it would be appreciated. I dont need it fluffed up, just something that works...

Thanks..
:NaN:
Posted on 2003-07-22 22:43:57 by NaN
I haven't checked out Iczelion's tutorial but I would be surprised if he used LVM_SORTITEMSEX, I prefer this sorting callback as it passes two items and an lParam. The items can be compared for any parameter or subitem and returning -1,0,1 determines the placement. I use the LPARAM to pass the sort order and subitem number (hiword/loword) and that makes for a very powerful sorting routine. I posted a q&d routine on the board somewhere you should be able to find it in a search. Here it is (changed a little since then):
LVCompareFunc	PROTO	:DWORD,:DWORD,:DWORD

invoke SendMessage,hListView,LVM_SORTITEMSEX,TRUE,ADDR LVCompareFunc

LVCompareFunc proc lParam1:DWORD,lParam2:DWORD,lParamSort:DWORD
LOCAL szBuffer1[256] :BYTE
LOCAL szBuffer2[256] :BYTE
LOCAL ItemData :LV_ITEM

; lParamSort HiWord = Sort order (False = reverse order)
; lParamSort LoWord = Column to sort

mov eax,lParam1
mov ItemData.iItem,eax
mov eax,lParamSort
and eax,0FFFFh
mov ItemData.iSubItem,eax
lea eax,szBuffer1
mov ItemData.pszText,eax
mov ItemData.cchTextMax,255
mov ItemData.imask,LVIF_TEXT
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

mov eax,lParam2
mov ItemData.iItem,eax
mov eax,lParamSort
and eax,0FFFFh
mov ItemData.iSubItem,eax
lea eax,szBuffer2
mov ItemData.pszText,eax
mov ItemData.cchTextMax,255
mov ItemData.imask,LVIF_TEXT
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

invoke lstrcmp,ADDR szBuffer1,ADDR szBuffer2

and lParamSort,0FFFF0000h
.IF lParamSort == FALSE
neg eax
.ENDIF

ret
LVCompareFunc endp
Posted on 2003-07-22 23:19:19 by donkey
LVM_SORTITEMSEX is better than older LVM_SORTITEMS, but it requires IE 5.0 to be installed for WinNT and Win9x. This may be a problem.
Posted on 2003-07-23 01:16:45 by japheth
If you don't use lParam, you can store the item index there, and LVM_SORTITEMS will work exactly like LVM_SORTITEMSEX. That would make your program compatible with all Explorer versions.
Posted on 2003-07-23 12:28:29 by QvasiModo
NaN, this sounds similar to a problem I was having. When you add a new item to a sorted list it might not get added in the position you were expecting, therefore if you try to add extra columns things get mixed up.

I think when you insert an item it returns the idex which you then use to add the extra column info. Perhaps your problem is completely unrelated, but it sounds similar.
Posted on 2003-07-23 13:04:40 by Eóin

I haven't checked out Iczelion's tutorial but I would be surprised if he used LVM_SORTITEMSEX, I prefer this sorting callback as it passes two items and an lParam. The items can be compared for any parameter or subitem and returning -1,0,1 determines the placement. I use the LPARAM to pass the sort order and subitem number (hiword/loword) and that makes for a very powerful sorting routine. I posted a q&d routine on the board somewhere you should be able to find it in a search. Here it is (changed a little since then):
LVCompareFunc	PROTO	:DWORD,:DWORD,:DWORD

invoke SendMessage,hListView,LVM_SORTITEMSEX,TRUE,ADDR LVCompareFunc

LVCompareFunc proc lParam1:DWORD,lParam2:DWORD,lParamSort:DWORD
LOCAL szBuffer1[256] :BYTE
LOCAL szBuffer2[256] :BYTE
LOCAL ItemData :LV_ITEM

; lParamSort HiWord = Sort order (False = reverse order)
; lParamSort LoWord = Column to sort

mov eax,lParam1
mov ItemData.iItem,eax
mov eax,lParamSort
and eax,0FFFFh
mov ItemData.iSubItem,eax
lea eax,szBuffer1
mov ItemData.pszText,eax
mov ItemData.cchTextMax,255
mov ItemData.imask,LVIF_TEXT
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

mov eax,lParam2
mov ItemData.iItem,eax
mov eax,lParamSort
and eax,0FFFFh
mov ItemData.iSubItem,eax
lea eax,szBuffer2
mov ItemData.pszText,eax
mov ItemData.cchTextMax,255
mov ItemData.imask,LVIF_TEXT
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

invoke lstrcmp,ADDR szBuffer1,ADDR szBuffer2

and lParamSort,0FFFF0000h
.IF lParamSort == FALSE
neg eax
.ENDIF

ret
LVCompareFunc endp


Can i ask what M$'s logic was to NOT provide the handle in the call back to sort with?? Does anyone have a guess? Even your above source assume a global "hListView" for sending messages in the sorting callback. I think its dumb i have to prepare a global handle for this! Is there any better way?

:NaN:
Posted on 2003-07-23 17:59:29 by NaN
I don't know if it's really better, but you could allocate some memory, put in there all the data you need (including the control's handle), and put a pointer to all this data in the lParam field of the LV_ITEM structure. That way, you can access it from the sorting procedure.
I know, it's not nice, but that's Micro$oft's fault :grin:
Posted on 2003-07-23 18:07:05 by QvasiModo



Can i ask what M$'s logic was to NOT provide the handle in the call back to sort with?? Does anyone have a guess? Even your above source assume a global "hListView" for sending messages in the sorting callback. I think its dumb i have to prepare a global handle for this! Is there any better way?

:NaN:

I agree with you here, it is very inconvienient not to have the handle sent when you have multiple list views. I have been frustrated by this as well. QvasiModo's idea is a great one, pass the offset of a structure in LPARAM with all the info you need. I hadn't thought of that and will definitely be using it in the future.
Posted on 2003-07-23 18:17:03 by donkey
By the way, Nan, are you adding the subitems with the LVM_SETITEM message? Because when experimenting with listview controls I found (by accident :grin: ) that you can add any number of subitems by just responding to callback and custom draw messages... So if you can store the item and subitems data in memory and place a pointer in lParam, maybe the problem with the subitems could be solved.
Posted on 2003-07-24 08:08:29 by QvasiModo
Hi AceEmbler,

Weird, I was just working on that today again. I am translating my code library to GoAsm as that is now my primary assembler and I was doing the listview today :) This is an updated algo for it, you can still use your pop handle if you want :

LVSortParam FRAME lParam1,lParam2,lParamSort

uses ebx,edi
LOCAL ItemData1 :LV_ITEM
LOCAL ItemData2 :LV_ITEM

; lParamSort low order word = rev/fwd true/false, high order = column number
movzx ebx,W[lParamSort+2]
movzx edi,W[lParamSort]

mov eax,[lParam1]
mov [ItemData1.iItem],eax
mov [ItemData1.iSubItem],ebx
mov D[ItemData1.imask],LVIF_PARAM
invoke SendMessage,[hListView],LVM_GETITEM,0,OFFSET ItemData1

mov eax,[lParam2]
mov [ItemData2.iItem],eax
mov [ItemData2.iSubItem],ebx
mov D[ItemData2.imask],LVIF_PARAM
invoke SendMessage,[hListView],LVM_GETITEM,0,OFFSET ItemData2

mov eax,[ItemData2.lParam]
mov ecx,[ItemData1.lParam]
cmp ecx,eax
jae >
mov eax,-1
jmp >L1
:
jne >
mov eax,0
jmp >L1
:
mov eax,1
L1:
or edi,edi
jz >
neg eax
:
ret
ENDF

LVSortText FRAME lParam1,lParam2,lParamSort
uses ebx,edi
LOCAL ItemData :LV_ITEM
LOCAL Tbuff1[256] :B
LOCAL Tbuff2[256] :B

; lParamSort low order word = rev/fwd true/false, high order = column number
movzx ebx,W[lParamSort+2]
movzx edi,W[lParamSort]

mov eax,[lParam1]
mov [ItemData.iItem],eax
mov [ItemData.iSubItem],ebx
mov D[ItemData.imask],LVIF_TEXT
lea eax,Tbuff1
mov [ItemData.pszText],eax
mov D[ItemData.cchTextMax],255
invoke SendMessage,[hListView],LVM_GETITEM,0,OFFSET ItemData

mov eax,[lParam2]
mov [ItemData.iItem],eax
mov [ItemData.iSubItem],ebx
mov D[ItemData.imask],LVIF_TEXT
lea eax,Tbuff2
mov [ItemData.pszText],eax
mov D[ItemData.cchTextMax],255
invoke SendMessage,[hListView],LVM_GETITEM,0,OFFSET ItemData

invoke lstrcmpi,OFFSET Tbuff1,OFFSET Tbuff2
or edi,edi
jz >
neg eax
:
ret

ENDF
Posted on 2004-01-13 19:23:00 by donkey
Hi AceEmbler,

The sort callback is a recursive routine, the way you have it set up it will pop a dword everytime it is called (maybe hundreds) but you only push one dword. It doesn't seem like it can work.
Posted on 2004-01-13 22:58:55 by donkey
Another simple solution is to store the sort parameter in GWL_USERDATA and pass the hListView to the CompareFunc. :)
Posted on 2004-01-14 01:28:02 by Morris

Hi AceEmbler,

The sort callback is a recursive routine, the way you have it set up it will pop a dword everytime it is called (maybe hundreds) but you only push one dword. It doesn't seem like it can work.



heh I was just thinking why my ListView is sorted incorectly :tongue:

thx Moris i was thinking about this also but i couldnt remember func to set up Window associated number so i just giveup.
Posted on 2004-01-14 01:40:21 by AceEmbler

Another simple solution is to store the sort parameter in GWL_USERDATA and pass the hListView to the CompareFunc. :)


The index numbers are passed to the compare proc by windows, it is a callback routine and you do not get the choice of the parameters passed. If you want columns/direction in lParamSort you cannot have the handle, if you do not need these then pass the handle in lParamSort. If you want both then you will have to use a global variable, that is much faster than setting GWL_USERDATA every time and reading it up to 4 or 5 times per item depending on how many recursions are necessary for the sort. It's easiest to just use a global. I had hoped to find hListview on the stack ( from the SendMessage function) but it appears to have been popped before the call to the routine.
Posted on 2004-01-14 08:49:26 by donkey
Donkey are you sure it's working. I made an Masm version and it's not working correctly.


LVSortText proc uses ebx edi lParam1,lParam2,lParamSort
LOCAL ItemData :LV_ITEM
LOCAL Tbuff1[256] :BYTE
LOCAL Tbuff2[256] :BYTE

; lParamSort low order word = rev/fwd true/false, high order = column number
movzx ebx,word ptr
movzx edi,word ptr

mov eax,
mov ,eax
mov ,ebx
mov ,LVIF_TEXT
lea eax,Tbuff1
mov ,eax
mov ,dword ptr 255
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

mov eax,
mov ,eax
mov ,ebx
mov ,LVIF_TEXT
lea eax,Tbuff2
mov ,eax
mov ,dword ptr 255
invoke SendMessage,hListView,LVM_GETITEM,0,ADDR ItemData

invoke lstrcmpi,ADDR Tbuff1,ADDR Tbuff2
or edi,edi
jz @F
neg eax
@@:
ret
LVSortText endp


lParamSort in my code is 0 or 1, my list view raport have one column but it's not working corectly. I dont know how is it sorted
:tongue:
Posted on 2004-01-14 13:16:25 by AceEmbler
Yes, it is tested and working. Do not attempt to pop the listview handle, you are screwing up the stack. Everything else looks OK to me. Here is my listview testbed, it demos the application :
Posted on 2004-01-14 13:42:08 by donkey

Yes, it is tested and working. Do not attempt to pop the listview handle, you are screwing up the stack. Everything else looks OK to me. Here is my listview testbed, it demos the application :


It just came to my mind how stupid it was to push handle like this :P
Posted on 2004-01-14 13:50:39 by AceEmbler
Hi AceEmbler,

If you really need the handle passed with the list view just put it in a structure and pass the pointer:

LVSORT struct

handle dd
direction dd
column dd
ENDS

.data
lvsort LVSORT <>

.code

mov eax,[hListView]
mov [lvsort.handle],eax
mov [lvsort.direction],FALSE
mov [lvsort.column],1
invoke SendMessage, [hListView], LVM_SORTITEMSEX, OFFSET lvsort, OFFSET LVSortText


Then change the following:

LVSortParam FRAME lParam1,lParam2,lParamSort

uses ebx,edi,esi
LOCAL ItemData1 :LV_ITEM
LOCAL ItemData2 :LV_ITEM

; lParamSort pointer to LVSORT structure
mov edi,[lParamSort]
mov ebx,[edi+LVSORT.column]
mov esi,[edi+LVSORT.handle]
mov edi,[edi+LVSORT.direction]


Use ESI in place of the listview handle

You could even adapt it to have different sort keys based on fourth member.
Posted on 2004-01-14 17:13:22 by donkey
Here, I haven't tested this but it should work fine, it combines the routines into one big one and passes everything it needs in a struct.
LVSORT struct

handle dd
direction dd
column dd
type dd
ENDS

.data
lvsort LVSORT <>

.code

mov eax,[hListView]
mov [lvsort.handle],eax
mov [lvsort.direction],FALSE ; false = forward/ true = rev
mov [lvsort.column],1
mov D[lvsort.type],0 ; 1 = text
invoke SendMessage, [hListView], LVM_SORTITEMSEX, OFFSET lvsort, OFFSET LVSortProc

LVSortProc FRAME lParam1,lParam2,lParamSort
uses ebx,edi,esi
LOCAL ItemData1 :LV_ITEM
LOCAL ItemData2 :LV_ITEM
LOCAL Tbuff1[256] :B
LOCAL Tbuff2[256] :B

; lParamSort low order word = rev/fwd true/false, high order = column number
mov edi,[lParamSort]
mov ebx,[edi+LVSORT.column]
mov esi,[edi+LVSORT.handle]

mov eax,[lParam1]
mov [ItemData1.iItem],eax
mov [ItemData1.iSubItem],ebx
mov D[ItemData1.imask],LVIF_TEXT + LVIF_PARAM
lea eax,Tbuff1
mov [ItemData1.pszText],eax
mov D[ItemData1.cchTextMax],255
invoke SendMessage,esi,LVM_GETITEM,0,OFFSET ItemData1

mov eax,[lParam2]
mov [ItemData2.iItem],eax
mov [ItemData2.iSubItem],ebx
mov D[ItemData2.imask],LVIF_TEXT + LVIF_PARAM
lea eax,Tbuff2
mov [ItemData2.pszText],eax
mov D[ItemData2.cchTextMax],255
invoke SendMessage,esi,LVM_GETITEM,0,OFFSET ItemData2

cmp D[edi+LVSORT.type],1
mov edi,[edi+LVSORT.direction]
jne >S1
invoke lstrcmpi,OFFSET Tbuff1,OFFSET Tbuff2
or edi,edi
jz >
neg eax
:
ret

S1:
mov eax,[ItemData2.lParam]
mov ecx,[ItemData1.lParam]
cmp ecx,eax
jae >
mov eax,-1
jmp >L1
:
jne >
mov eax,0
jmp >L1
:
mov eax,1
L1:
or edi,edi
jz >
neg eax
:
ret
ENDF
Posted on 2004-01-14 17:34:11 by donkey