News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

ImageList_Draw

Started by Grincheux, September 20, 2008, 06:00:09 PM

Previous topic - Next topic

Grincheux

How do you use "ImageList_Draw" ?

I have created a ListView and an ImageList. I have associated the ImageList with the ListView.

I have many images files that I want to Draw into the ListView throught the ImageList.

The problem is that I don't know and don't understand how to draw each image ?

The image size to draw is 256 x 192.

Can someone help me ? :dazzled:
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

fearless

Here is an example of creating an imagelist and adding icons to it. I call this after my routine to initialize the listview creating it in report style. The imagelist is then linked/associated with the listview.

;**************************************************************************
; Initialize the ImageList Control - Call from WM_INITDIALOG event
;**************************************************************************
InitImageList PROC hWin:DWORD
LOCAL hILIcon:DWORD

invoke ImageList_Create,16,16,ILC_COLOR32,1,20
.IF eax != NULL
mov hImagelist, eax
.else
PrintText 'Failed to create imagelist'
.endif
invoke  SendMessage,hListview,LVM_SETIMAGELIST,LVSIL_SMALL,hImagelist

; Load icons and add to list
Invoke LoadIcon, hInstance, ICO_CSS
mov hILIcon, eax
.IF eax == NULL
PrintText 'Icon Not Loaded'
.endif
invoke ImageList_AddIcon,hImagelist,eax

Invoke LoadIcon, hInstance, ICO_HLD
mov hILIcon, eax
invoke ImageList_Add,hImagelist,hILIcon,NULL

Invoke LoadIcon, hInstance, ICO_DOD
mov hILIcon, eax
invoke ImageList_Add,hImagelist,hILIcon,NULL

Invoke LoadIcon, hInstance, ICO_TF2
mov hILIcon, eax
invoke ImageList_Add,hImagelist,hILIcon,NULL
ret
InitImageList endp


Later on in my program i call the following proc to add a list item and set the icon of the listitem ive just inserted:

;**************************************************************************
; Inserts an item into the listbox. LVItemText should be filled before this
; is called, as this is the buffer that is used.
;**************************************************************************
ListViewInsertItem PROC hWin:DWORD, nItem:DWORD
mov eax, nItem
mov LVItem.iItem, eax
mov LVItem.iSubItem,0
invoke SendMessage, hListview, LVM_INSERTITEM, 0, Addr LVItem
mov LVItem.iItem, eax
ret
ListViewInsertItem endp


thats just for inserting the text of the item, you can of course then set additional subitems:

;**************************************************************************
; Inserts an Sub-item into the listbox. LVItemText should be filled before this
; is called, as this is the buffer that is used. nSubItem is the index.
;**************************************************************************
ListViewInsertSubItem PROC hWin:DWORD, nSubItem:DWORD
mov eax, nSubItem
mov LVItem.iSubItem, eax
invoke SendMessage, hListview, LVM_SETITEM, 0, addr LVItem
ret
ListViewInsertSubItem endp


and the icon to show for this list item/sub items row:

;**************************************************************************
; Sets the icon of the specified index.
;**************************************************************************
ListViewSetItemIcon PROC hWin:DWORD, nItem:DWORD, nIcon:DWORD
mov eax, nIcon
mov LVItem.iImage, eax
invoke SendMessage, hListview, LVM_SETITEM, nItem, Addr LVItem
ret
ListViewSetItemIcon endp


this is the data and data? sections:

.DATA
LVItemText db MAX_PATH dup (0) ; Buffer to hold text to display when used with LVItem
LVItem LV_ITEM <LVIF_TEXT,1,0,LVIS_FOCUSED,0,LVItemText,MAX_PATH,0,0>


.DATA?
hListview dd ? ; Listview handle
hImagelist dd ? ; ImageList handle


so all i need do to set an item, sub item and icon is, something like this:

Invoke lstrcpy, Addr LVItemText, CTEXT("This is the listitem")
Invoke ListViewInsertItem,hWin, 0
Invoke lstrcpy, Addr LVItemText, CTEXT("This is the subitem, column2 of the same listitem if you will")
Invoke ListViewInsertSubItem, hWin, 1
Invoke ListViewSetItemIcon, hWin, 0, 0 ; the last 0 is the 0 based index of the icons loaded into our imagelist, in this case the ICO_CSS resource.


For larger icons, or images with the listview being in tile mode or icon mode you probably want to look at setting another imagelist with larger icons/images and look at ImageList_Create and LVM_SETIMAGELIST for more details. Hope that helps give you an idea on how to add images to the listview.
ƒearless

Grincheux

This is very clear, better than MS does !

With these infos I can make what I expected.
In fact, in the previous version of my program I used an ownerdraw listbox and I want to replace it with a listview.

In the examples the files ae loaded then added to the listview, is it windows which is responsible for drawing the image ?

My listview has only one row in which we can find the image. Do I have to call ImageList_Draw ?
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

fearless

I dont use a ownerdrawn listview at all in my examples. Also no need for ImageList_Draw in this example for what we want to do. The listview will automatically use the imagelist images and draw them itself on every refresh/paint for each listitem shown as specified for each listitems icon, which is an index into the imagelist.

It does all this itself. So yes in the examples, you can just load up the images you wish to use in the imagelist either by a resource compiled into your program and then loaded with LoadIcon or LoadBitmap or from files on disk at runtime with the appropriate code to assign them/add them to the imagelist before populating your listview with its listitems.
ƒearless

six_L

;**************************************************************************
; Sets the icon of the specified index.
;**************************************************************************
ListViewSetItemIcon PROC hWin:DWORD, nItem:DWORD, nIcon:DWORD
mov eax, nIcon
mov LVItem.iImage, eax
invoke SendMessage, hListview, LVM_SETITEM, nItem, Addr LVItem
ret
ListViewSetItemIcon endp

This always set the frist Column of listview iImage.
regards

fearless

Yes that is correct, for my code, i only ever need to set the first column as the listview is always set to report style in my program.
ƒearless

Grincheux

With the ewamples and he help found here my program wroks well.

But I have an other problem : In a folder I have more than 4 000 images. The ImageList_Add returns -1 after loading the 3 000th !
I think that it is normal because there too many images.
So I decided to use the I_IMAGECALLBACK flag into the LVITEM struct.
If I use it I think the problem will always be the same !

What is the solution ?

Thanks
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Grincheux

Now I can show images, but I have an other problem : When I click (NM_CLICK message) on a image it is not shown !
I thought that the listview would receive a LVN_GETDISPINFO message, but no !

How could I make to draw an other bitmap when the user selects an image ?

I think that I would have to use the LVN_ITEMCHANGING message, but is it a good idea ?

You can download the code and binary at : http://idcat39.com/download/Image'In.exe

Any help would be welcome. :U
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

fearless

For handling large amounts of listview items you will probably need to create a virtual listview, see the MSDN documentation for more details, about half way down this page: http://msdn.microsoft.com/en-us/library/bb774735(VS.85).aspx

With the virtual listview you have to handle more of the processing of the listview itself.

For drawing a bitmap from the selected listview, you can re-read the index in the imagelist getting the currently selected items index.

By setting the lParam of the LV_ITEM structure when you add an item you can specify a dword value here. this can be your index to your imagelist icon/bitmap. So read the selected items lParams value and use this to get the icon/bitmap from the imagelist and then handle the drawing of it on screen. You could have two imagelists - 1 for smaller icons (thumbnails if you like) and then another imagelist that stores the larger images perhaps.

hope that helps
ƒearless

fearless

Also looking at your code, yes you could use the LVN_ITEMCHANGED or LVN_ITEMCHANGING notifications, i use the following type code to indicate a change and get the last item selected and the newer one. I use it in a program to change a selected game profile (which are listed in my listview)

.elseif eax == LVN_ITEMCHANGING
assume edi:ptr NM_LISTVIEW
mov eax, [edi+NM_LISTVIEW.iItem]
mov edx, [edi+NMLISTVIEW.uNewState]
.IF edx != LVIS_FOCUSED or LVIS_SELECTED
mov ListViewSelectedItemFrom, eax ; save the item that was last selected / focused, in case we need to do something with it
.ENDIF
assume edi:ptr NMHDR

.elseif eax == LVN_ITEMCHANGED
assume edi:ptr NM_LISTVIEW
mov eax, [edi+NM_LISTVIEW.iItem]

mov edx, [edi+NMLISTVIEW.uNewState]
.IF edx == LVIS_FOCUSED or LVIS_SELECTED
mov ListViewSelectedItemTo, eax ; save the newly selected / focused item now
.ENDIF

; Call some function here to process the last item which has lost focus and new item that was selected perhaps...

assume edi:ptr NMHDR

ƒearless

Grincheux

Very nice web site Fearless !

I have an ImageList with 1024 into which I can store images.
I tested it with a folder into which I had more than 7000 images and I don't get any problem.

When I receive the ILVM_ITEMCHANGED, I replace the old image with a new one having a white border rather than a black border but I don't see it on the screen !
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

fearless

The only thing i can think of doing is possibly using LVM_REDRAWITEMS message to force an item refresh. Although in the win32.hlp it says:
QuoteThe specified items are not actually redrawn until the list view window receives a WM_PAINT message to repaint. To repaint immediately, call the UpdateWindow function after using this macro.

I would think it will redraw all the listitems visible on screen in the listview, which may then show the replaced image. I take it you are exchanging the white border image into the imagelist with its previous un-selected image, then re-setting the LV_ITEM of the listview item to associate the new image.

There may be another way of simply drawing a border around the item, by setting the listview to owner drawn state, getting the image, using FrameRect and Rectangle functions. Ive havn't any experience of processing the listview like that im afraid, so its only a guess but thats what id look into.

Cant take all the credit for the our Counterstrike Source clan website - the main page which does like nice was done by a friend of mine a clan member named Cocaine - i only modded the forums, but im in the middle of updating a blog site for coding and development at www.newworldorder.org.uk/fearless/
ƒearless

fearless

Another possible option is drawing the colour of the selected listitem to show the selection as another colour. I use the following code to alternate a light blue colour for all my rows, but you code easily adapt the code to draw only the selected items row colour (in tile view i think this will draw the background colour differently - not sure, anyhow the code could be useful)

.elseif eax == NM_CUSTOMDRAW ; Custom draw, alt rows? or something else
assume edi:ptr NMLVCUSTOMDRAW
           
mov eax,[edi+NMLVCUSTOMDRAW.nmcd.dwDrawStage]
.if eax == CDDS_SUBITEM || CDDS_ITEMPREPAINT

; Alternate Row Colour
mov esi,[edi+NMLVCUSTOMDRAW.nmcd.dwItemSpec]
.if  _mod(esi,2) == 0 ; Calc mod of item to see if the background should be applied
mov [edi].clrTextBk,00FFF3F2h; background colour
mov [edi].clrText,00000000h   ; text colour = black
.else
mov [edi].clrTextBk,0FFFFFFh  ;Background text = white
mov [edi].clrText,00h         ; text colour = black
.endif
           
; Current line
mov eax,[edi+NMLVCUSTOMDRAW.nmcd.dwItemSpec]
Invoke SendMessage, hListview, LVM_GETITEMSTATE, eax, 2
.if eax == 2
mov LVItem.state, 12
mov LVItem.stateMask, 16
mov ebx,[edi+NMLVCUSTOMDRAW.nmcd.dwItemSpec]
mov [edi].clrTextBk,03F3F3Fh 
mov [edi].clrText,303h       
.endif
           
return CDRF_NOTIFYSUBITEMDRAW
.endif


it uses the _mod macro to get determine alternate rows to colour/draw:

;**************************************************************************
; MOD Macro
;**************************************************************************
_mod MACRO val1:REQ, val2:REQ
push ecx
mov  eax,val1
mov  ecx,val2
xor  edx,edx
div  ecx 
pop ecx
exitm <edx>
endm
ƒearless