I usually try and avoid the common controls like the plague as I dislike the interface for most of them but I needed to use a list view set up as close as possible to and old fashion listbox. I created the list view control with CreateWindowEx, set it up with one column and loaded some test items into it. To get the items from the list I made a subclass processing the WM_LBUTTONDBLCLICK message, fed the X Y co-ordinates into a LV_HITTESTINFO.POINT structure, retreived the index of the selected item with LVM_GETITEM and everything works except for one quirk, I must first click on the header button to get the text to display otherwise I get garbage back.
This is the subclass.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
LviewProc proc hCtl :DWORD,
uMsg :DWORD,
wParam :DWORD,
lParam :DWORD
LOCAL pbuf :DWORD
LOCAL buffer[128]:BYTE
LOCAL lvi :LV_ITEM
LOCAL lht :LV_HITTESTINFO
.if uMsg == WM_COMMAND
.elseif uMsg == WM_LBUTTONDBLCLK
movsx eax, word ptr [ebp+20]
mov lht.pt.x, eax
movsx eax, word ptr [ebp+22]
mov lht.pt.y, eax
lea eax, buffer
mov lvi.pszText, eax
mov lvi.cchTextMax, 128
invoke SendMessage,hLview,LVM_HITTEST,0,ADDR lht ; get index of selection
mov lvi.iItem, eax
invoke SendMessage,hCtl,LVM_GETITEM,0,ADDR lvi
fn MessageBox,hCtl,lvi.pszText,"Title",MB_OK
.endif
invoke CallWindowProc,lpLviewProc,hCtl,uMsg,wParam,lParam
ret
LviewProc endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
I want to run the list view control without a header button using the LVS_NOCOLUMNHEADER style but if I remove it, I have no way of activating the text and just get garbage back.
Has anyone run into this problem before ?
I haven't run into this specific problem before, but I would be inclined to use LVM_GETNEXTITEM (http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/listview/messages/lvm_getnextitem.asp) to search for the first selected and focused item rather than a hit-test. I've never had much success using hit-testing methods in VB before.
Cheers,
Zooba :U
Hutch,
Take a look at the attachment to see how I did my old style listbox. Doubleclick any lineitem to see the text returned. It may not be exactly what you are looking for but it sure works without a header.
Paul
[attachment deleted by admin]
Why resort to subclassing? You could catch double clicks from the main message procedure as well. Something like:
.if uMsg == WM_NOTIFY
mov edi, lParam
mov eax, [edi.NMHDR].hwndFrom
.if eax == [hListView]
.if [edi.NMHDR].code == NM_DBLCLK
invoke SendMessage, [hListView], LVM_GETNEXTITEM, -1, LVNI_SELECTED
cmp eax, -1
je @f
mov lvi.iItem, eax
mov lvi.iSubItem, 0
mov lvi.imask, LVIF_TEXT
lea eax, tmp_buffer
mov lvi.pszText, eax
mov lvi.cchTextMax, tmp_buffer_size
invoke SendMessage, [hListView], LVM_GETITEM, 0, ADDR lvi
@@:
.endif
.endif
.endif
Arafel,
That is EXACTLY what I did in the attached project above. Catch double clicks and no subclassing and return text. Take a peek at it. I like it when people think along the same lines.
Paul
Hehe, sorry Paul. I guess I was too hasty, so I didn't check the attachments.
arafel,
Actually, I was happy to see your posting. It gives me the idea that my method might be useful. :U
Paul
Hi Paul,
I haven't checked the attachments either because I am at work right now but in WinExplorer I do some pretty interesting things to listviews without any subclassing, context menus and the sort. I wrote a whole slew of demos and test beds for them while I was figuring them out, I may just end up doing a little tutorial on listviews from the basics right up to virtual listviews and custom drawn ones. Would you be interested in participating in a tutorial if I do one ? (MASM/GoAsm formats). Actually a full common controls tutorial by control type would be a more ambitious project.
Edgar,
That would be a very useful tutorial for address books, tables and such. I would be happy to help.
Paul
A complete "reading my mind" here!!! Will like to help !!!
After struggling with listviews, treeviews, owner drawn buttons, toolbars (and having succeded beacuse of the gracious helping on MASM Forum and of course Iczelion's tutorials) I was thinking the same.
What I was thinking was a bit more evil :green2 ...take each and every control, rip it to shreds, chew it up and explain it nicely as to what it is...how it works, etc...more on the lines from a UI Designer perspective
Obviously the first "control", if any should be the dialog itself. I remember many hours spent because either some teeny-tiny style was not set or (the DlgProc not written correctly)
Hutch,
as you surely know but possibly forgot: the LVITEM structure has a "mask" (or "imask") field where you have to tell windows what fields you want to be filled. Seems this field isn't filled in your code.
Regards
This scruffy mess actually works, probably one of the worst interfaces to a control i have ever seen.
case WM_NOTIFY
mov edx, lParam
mov eax, (NMHDR PTR [edx]).hwndFrom
.if eax == hLview
mov eax, (NMHDR PTR [edx]).code
.if eax == NM_DBLCLK
invoke GetCursorPos,ADDR pt
invoke ScreenToClient,hLview,ADDR pt
mov ecx, pt.x
mov edx, pt.y
mov lht.pt.x, ecx
mov lht.pt.y, edx
invoke SendMessage,hLview,LVM_HITTEST,0,ADDR lht ; get index of selection
mov lvi.imask, LVIF_TEXT
lea eax, buffer
mov lvi.pszText, eax
mov lvi.cchTextMax, 128
mov lvi.iSubItem, 0
invoke SendMessage,hLview,LVM_GETITEMTEXT,lht.iItem,ADDR lvi
fn MessageBox,hWin,lvi.pszText,str$(lht.iItem),MB_OK
.endif
.endif
Microsoft YUK !
hutch--,
Instead of code between GetCursorPos thru LVM_HITTEST, you should be able to use:
invoke SendMessage, [hList], LVM_GETNEXTITEM, -1, LVNI_FOCUSED ;find the item that has the focus
mov [lvi.iItem], eax
hth,
farrier
Edit: This will only give the row, and not the row and column which is what you wanted :red
ReEdit: Actually, it might work, since you specify mov lvi.iSubItem, 0 :red
Is there a reason your doing a hittest?
I do it like this:
.elseif uMsg == WM_NOTIFY
mov esi,lParam
assume esi:ptr NMHDR
.if [esi].NMHDR.code == NM_DBLCLK
mov eax,[esi].NMHDR.hwndFrom
.if eax == hListView
invoke SendMessage,hListView,LVM_GETNEXTITEM,-1,LVNI_FOCUSED
or eax,eax
js No_Good
push eax
mov [lvi.iItem],eax
mov [lvi.iSubItem],0
mov [lvi.imask],LVIF_PARAM or LVIF_TEXT
mov [lvi.pszText],offset szFileName
mov [lvi.cchTextMax],MAX_PATH
mov byte ptr[szFileName],0
invoke SendMessage,hListView,LVM_GETITEM,0,addr lvi
pop eax
mov [lvi.iItem],eax
mov [lvi.iSubItem],2
mov [lvi.imask],LVIF_PARAM or LVIF_TEXT
mov [lvi.pszText],offset szFileType
mov [lvi.cchTextMax],MAX_PATH
mov byte ptr[szFileType],0
invoke SendMessage,hListView,LVM_GETITEM,0,addr lvi
; at this point the item string is in szFileName
.endif
.endif
No_Good:
If there is a reason for doing a hittest then ignore this code. :bg
Zcoder....
Hi Hutch,
I have found that with listviews the CLICK messages can be slightly delayed, making GetCursorPos unreliable. I use the following for a hit test in WM_NOTIFY where EDI contains the value in lParam (for listviews only - treeviews do not seem to suffer from the same issue)
invoke GetMessagePos
movzx edx,ax
mov D[lvhi.pt.x],edx
shr eax,16
and eax,0FFFFh
mov D[lvhi.pt.y],eax
invoke ScreenToClient,[edi+NMHDR.hwndFrom],OFFSET lvhi.pt
invoke SendMessage,[edi+NMHDR.hwndFrom],LVM_HITTEST,0,OFFSET lvhi
mov eax,[lvhi.iItem]
test eax,eax
js >.NOITEM
; perform a LVM_GETNEXTITEM to determine which item if any has the focus
invoke SendMessage,[edi+NMHDR.hwndFrom],LVM_GETNEXTITEM,-1,LVNI_FOCUSED
test eax,eax
js >.NOITEM
BTW if you use GetCursorPos there is no need for 2 POINT structures...
invoke GetCursorPos,ADDR lht.pt
invoke ScreenToClient,hLview,ADDR lht.pt
invoke SendMessage,hLview,LVM_HITTEST,0,ADDR lht ; get index of selection
Donkey,
Thanks, for the test piece I wanted the LVM_HITTEST method as the task I had in mind was mouse based. Using the lht.pt structure is a good idea that saves the extra code.
Hi Hutch,
My pleasure. I generally only use the HITTEST to ensure that the user has actually clicked on an item then determine the item number using GETNEXTITEM. Otherwise the routine is called for any click inside the listview and if you set it to retain the focus using GETNEXTITEM will return the wrong item number. The example I posted is the most reliable way I have found to determine if first - the user actually selected an item and second - what the item number is. Every other way I attempted to solve this issue had it's own particular quirks.
In my WM_NOTIFY handler, this handles sort--thanks to donkey--,Enter Key, Single Click, and Double Click to process ListView line with focus, or line Clicked or DoubleClicked.
wmnotify:
.else
mov eax, NMHDR.hwndFrom ;NMHDR.hwndFrom
.if eax, e, [hList]
.if NMHDR.code, e, LVN_COLUMNCLICK
.if NM_LISTVIEW.iSubItem, e, 1
;sort listview here
.endif
.elseif NMHDR.code, e, LVN_KEYDOWN
.if NMLVKEYDOWN.wVKey, e, VK_RETURN
jmp .display_line
.endif
.elseif NMHDR.code, e, NM_CLICK
jmp .display_line
.elseif NMHDR.code, e, NM_DBLCLK
jmp .display_line
.endif
.endif
.endif
jmp .finish
.display_line:
invoke SendMessage, [hList], LVM_GETNEXTITEM, -1, LVNI_FOCUSED ;find the item that has the focus, was clicked on
mov [.lvi.iItem], eax
mov [.lvi.iSubItem], 0
mov [.lvi.imask], LVIF_TEXT
lea eax, [.buffer]
mov [.lvi.pszText], eax
mov [.lvi.cchTextMax], 256
lea eax, [.lvi]
invoke SendMessage, [hList], LVM_GETITEM, 0, eax ;gets info from listview
farrier