ListIcon and image transparency

Windows specific forum
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

ListIcon and image transparency

Post by tatanas »

Hi,

I've got a problem with the transparency of image inside a Listicon.
If I use SetGadgetColor(ListIcon, #PB_Gadget_BackColor, ...) there is no problem with the image.
If I use SetGadgetItemColor(ListView, i, #PB_Gadget_BackColor, ...) (inside the AlternateListViewBackColor procedure) the image is not transparent anymore.

Here is a sample :

Code: Select all

Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
	Protected hIcon, img_index = -1
 	hIcon = ExtractIcon_(#Null, DLL_Path, index)
	If hIcon
		img_index = ImageList_AddIcon_(ImgList, hIcon)
		DestroyIcon_(hIcon)
	Else
		ProcedureReturn -1
	EndIf
	ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
	Protected itemLV.LVITEM ; tagLVITEM
	itemLV\mask = #LVIF_IMAGE
	itemLV\iItem = index_item
	itemLV\iSubItem = index_subitem
	itemLV\iImage = index_image
	If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
		Debug "#LVM_SETITEM error"
	EndIf
EndProcedure

Procedure AlternateListViewBackColor(ListView, Color = $F5F5F5)
	Protected i
	For i = 0 To CountGadgetItems(ListView) - 1
		If i & 1
			SetGadgetItemColor(ListView, i, #PB_Gadget_BackColor , Color, #PB_All)
		EndIf
	Next
EndProcedure


Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Define ListIcon = ListIconGadget(#PB_Any, 34, 30, 388, 402, "Col 1", 100)
AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)
; SetGadgetColor(ListIcon, #PB_Gadget_BackColor, RGB(190, 190, 190)) ; UNCOMMENT THIS LINE IF THE NEXT AlternateListViewBackColor(...) IS COMMENTED

Define ImgList = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)

For i = 0 To 10
	AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
	AlternateListViewBackColor(ListIcon) ; COMMENT THIS LINE IF THE PREVIOUS SETGADGETCOLOR(...) IS UNCOMMENTED
Next

icon_index = Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 5, 2, icon_index)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Windows 10 Pro x64
PureBasic 6.20 x64
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: ListIcon and image transparency

Post by RASHAD »

Code: Select all

Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
  Protected hIcon, img_index = -1
  hIcon = ExtractIcon_(#Null, DLL_Path, index)
  StartDrawing(ImageOutput(1))
  Box(0,0,24,24,$ffffff)
  DrawImage(hicon,0,0,24,24)
  StopDrawing()
  iinf.ICONINFO
  iinf\fIcon = 1
  iinf\hbmMask = ImageID(0)
  iinf\hbmColor = ImageID(1)
  icoHnd = CreateIconIndirect_(iinf)
  If icoHnd
    img_index = ImageList_AddIcon_(ImgList, icoHnd)
    DestroyIcon_(hIcon)
    DestroyIcon_(icoHnd)
  Else
    ProcedureReturn -1
  EndIf
  ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
  Protected itemLV.LVITEM ; tagLVITEM
  itemLV\mask = #LVIF_IMAGE
  itemLV\iItem = index_item
  itemLV\iSubItem = index_subitem
  itemLV\iImage = index_image
  If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
    Debug "#LVM_SETITEM error"
  EndIf
EndProcedure

Procedure AlternateListViewBackColor(ListView, Color = $F5F5F5)
  Protected i
  For i = 0 To CountGadgetItems(ListView) - 1
    If i & 1
      SetGadgetItemColor(ListView, i, #PB_Gadget_BackColor , Color, #PB_All)
    EndIf
  Next
EndProcedure

CreateImage(0,24,24)
CreateImage(1,24,24)
Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Define ListIcon = ListIconGadget(#PB_Any, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect)
AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)
; SetGadgetColor(ListIcon, #PB_Gadget_BackColor, RGB(190, 190, 190)) ; UNCOMMENT THIS LINE IF THE NEXT AlternateListViewBackColor(...) IS COMMENTED

Define ImgList = ImageList_Create_(24,24, #ILC_COLOR32|#ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)


For i = 0 To 10
  AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
  AlternateListViewBackColor(ListIcon) ; COMMENT THIS LINE IF THE PREVIOUS SETGADGETCOLOR(...) IS UNCOMMENTED
Next

icon_index = Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 5, 2, icon_index)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Egypt my love
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

Thank you RASHAD.
But on my PC, there is no transparency. Look at this screenshot :
https://ibb.co/mT0k37w

(I change the background color from $F5F5F5 to $E1E1E1 to boost the contrast)

EDIT : This topic https://stackoverflow.com/questions/632 ... -listviews seems to evoke the problem but the answer is not very clear for me...
Windows 10 Pro x64
PureBasic 6.20 x64
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: ListIcon and image transparency

Post by RASHAD »

Code: Select all


Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
  Protected hIcon, img_index = -1  
  iinf.ICONINFO
  hIcon = ExtractIcon_(#Null, DLL_Path, index)  
  StartDrawing(ImageOutput(1))
  Box(0,0,32,32,$FFFFFF) 
  DrawImage(hicon,0,0,24,24)
  StopDrawing()
  iinf\hbmMask = ImageID(0)
  iinf\hbmColor = ImageID(1)
  icoHnd = CreateIconIndirect_(iinf)
  ImageList_AddIcon_(ImgList, icoHnd) 
  StartDrawing(ImageOutput(1))
  Box(0,0,32,32,$3CB6FC) 
  DrawImage(hicon,0,0,24,24)
  StopDrawing()
  iinf\hbmMask = ImageID(0)
  iinf\hbmColor = ImageID(1)
  icoHnd = CreateIconIndirect_(iinf)
  ImageList_AddIcon_(ImgList, icoHnd) 
  DestroyIcon_(hIcon)
  DestroyIcon_(icoHnd)
  ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
  Protected itemLV.LVITEM ; tagLVITEM
  itemLV\mask = #LVIF_IMAGE
  itemLV\iItem = index_item
  itemLV\iSubItem = index_subitem
  itemLV\iImage = index_image
  If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
    Debug "#LVM_SETITEM error"
  EndIf
EndProcedure

Procedure AlternateListViewBackColor(ListView, Color = $3CB6FC)
  Protected i
  For i = 0 To CountGadgetItems(ListView) - 1
    If i & 1
      SetGadgetItemColor(ListView, i, #PB_Gadget_BackColor , Color, #PB_All)
    EndIf
  Next
EndProcedure

CreateImage(0,24,24)
CreateImage(1,24,24)
Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect)
AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)
; SetGadgetColor(ListIcon, #PB_Gadget_BackColor, RGB(190, 190, 190)) ; UNCOMMENT THIS LINE IF THE NEXT AlternateListViewBackColor(...) IS COMMENTED

Define ImgList = ImageList_Create_(24,24, #ILC_COLOR32|#ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)

For i = 0 To 10
  AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
  AlternateListViewBackColor(ListIcon) ; COMMENT THIS LINE IF THE PREVIOUS SETGADGETCOLOR(...) IS UNCOMMENTED
Next

Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 4, 2, 0)
ListIcon_SetItemImage(ListIcon, 5, 2, 1)
ListIcon_SetItemImage(ListIcon, 6, 2, 0)
ListIcon_SetItemImage(ListIcon, 7, 2, 1)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow

Egypt my love
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

Nice ! I don't know for you but it's almost good for me.
I've got a tiny offset (or resize) of the image (1 pixel vertical/height, 2 pixels horizontal/width).
Here is a screenshot : https://ibb.co/kXF9qn7
Windows 10 Pro x64
PureBasic 6.20 x64
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: ListIcon and image transparency

Post by RASHAD »

The problem comes from Windows XP theme
Next is a much easier method

Code: Select all

SetThemeAppProperties_(0)

Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
  Protected hIcon, img_index = -1
  hIcon = ExtractIcon_(#Null, DLL_Path, index)    
  StartDrawing(ImageOutput(1))
  Box(0,0,24,24,$FFFFFF) 
  DrawImage(hicon,0,0,24,24)
  StopDrawing()
  ImageList_Add_(ImgList,ImageID(1),ImageID(0)) 
  StartDrawing(ImageOutput(1))
  Box(0,0,24,24,$3CB6FC) 
  DrawImage(hicon,0,0,24,24)
  StopDrawing()
  ImageList_Add_(ImgList,ImageID(1),ImageID(0))
  DestroyIcon_(hIcon)
  ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
  Protected itemLV.LVITEM ; tagLVITEM
  itemLV\mask = #LVIF_IMAGE
  itemLV\iItem = index_item
  itemLV\iSubItem = index_subitem
  itemLV\iImage = index_image
  If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
    Debug "#LVM_SETITEM error"
  EndIf
EndProcedure

Procedure AlternateListViewBackColor(ListView, Color = $3CB6FC)
  Protected i
  For i = 0 To CountGadgetItems(ListView) - 1
    If i & 1
      SetGadgetItemColor(ListView, i, #PB_Gadget_BackColor , Color, #PB_All)
    EndIf
  Next
EndProcedure

CreateImage(0,24,24)
CreateImage(1,24,24)

SetThemeAppProperties_(1)
Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

SetThemeAppProperties_(2)
ListIconGadget(0, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect)

AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)
; SetGadgetColor(ListIcon, #PB_Gadget_BackColor, RGB(190, 190, 190)) ; UNCOMMENT THIS LINE IF THE NEXT AlternateListViewBackColor(...) IS COMMENTED

Define ImgList = ImageList_Create_(24,24, #ILC_COLOR32|#ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)
For i = 0 To 10
  AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
  AlternateListViewBackColor(ListIcon) ; COMMENT THIS LINE IF THE PREVIOUS SETGADGETCOLOR(...) IS UNCOMMENTED
Next

Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 4, 2, 0)
ListIcon_SetItemImage(ListIcon, 5, 2, 1)
ListIcon_SetItemImage(ListIcon, 6, 2, 0)
ListIcon_SetItemImage(ListIcon, 7, 2, 1)
SetThemeAppProperties_(1)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow

Edit :Modified for XP theme
Disable Modern Theme in Compiler Options
Last edited by RASHAD on Wed Mar 27, 2024 4:51 pm, edited 2 times in total.
Egypt my love
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

Sorry, I can't see any difference with the previous version.
Windows 10 Pro x64
PureBasic 6.20 x64
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: ListIcon and image transparency

Post by RASHAD »

Previous post modified
Egypt my love
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

Better but not perfect. The horizontal offet disappeared but the vertical one still remains.

Do you think it would be easier with WM_NOTIFY and CustomDraw ?
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
Caronte3D
Addict
Addict
Posts: 1361
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: ListIcon and image transparency

Post by Caronte3D »

tatanas wrote: Thu Mar 28, 2024 8:42 am ...but the vertical one still remains.
You must disable Modern Theme in Compiler Options as RASHAD said.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

That's what I did...
Windows 10 Pro x64
PureBasic 6.20 x64
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: ListIcon and image transparency

Post by breeze4me »

This is a trick using a background image.

Edit:
Fixed a drawing issue when returning from offscreen.

Code: Select all


Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
	Protected hIcon, img_index = -1
 	hIcon = ExtractIcon_(#Null, DLL_Path, index)
	If hIcon
		img_index = ImageList_AddIcon_(ImgList, hIcon)
		DestroyIcon_(hIcon)
	Else
		ProcedureReturn -1
	EndIf
	ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
	Protected itemLV.LVITEM ; tagLVITEM
	itemLV\mask = #LVIF_IMAGE
	itemLV\iItem = index_item
	itemLV\iSubItem = index_subitem
	itemLV\iImage = index_image
	If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
		Debug "#LVM_SETITEM error"
	EndIf
EndProcedure



Structure LVBKIMAGE Align #PB_Structure_AlignC
  ulFlags.l
  hbm.i
  pszImage.s
  cchImageMax.l
  xOffsetPercent.l
  yOffsetPercent.l 
EndStructure

#LVBKIF_SOURCE_NONE = 0
#LVBKIF_SOURCE_HBITMAP = 1
#LVBKIF_STYLE_TILE = 16

Global ListIcon
Global ImgListIconBk0, ImgListIconBk1
Global OldWndProc


Procedure SetListIconBkImage(Gadget, Image)
  Protected iResult, hdc, hdcDst, hBitmap, w, h, lvi.LVBKIMAGE, old
  
  If Image
    w = ImageWidth(Image)
    h = ImageHeight(Image)
    If w = 0 Or h = 0 : ProcedureReturn 0 :  EndIf
    
    ; https://learn.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-lvbkimagew
    ; #LVBKIF_SOURCE_HBITMAP
    ; "A background bitmap is supplied via the hbm member of LVBKIMAGE. If the message LVM_SETBKIMAGE succeeds, then the list-view takes ownership of the bitmap."
    
    ; The ListIconGadget takes ownership of the image, so you need to create a new bitmap and pass it over.
    ; The bitmap is internally destroyed when the image is replaced.
    hdc = StartDrawing(ImageOutput(Image))
    If hdc
      hdcDst = CreateCompatibleDC_(hdc)
      If hdcDst
        hBitmap = CreateCompatibleBitmap_(hdc, w, h)
        If hBitmap
          old = SelectObject_(hdcDst, hBitmap)
          BitBlt_(hdcDst, 0, 0, w, h, hdc, 0, 0, #SRCCOPY)
          SelectObject_(hdcDst, old)
          
          lvi\ulFlags = #LVBKIF_SOURCE_HBITMAP | #LVBKIF_STYLE_TILE
          lvi\hbm = hBitmap
          iResult = SendMessage_(GadgetID(Gadget), #LVM_SETBKIMAGE, 0, lvi)
        EndIf
        DeleteDC_(hdcDst)
      EndIf
      StopDrawing()
    EndIf
  EndIf
  ProcedureReturn iResult
EndProcedure

Procedure SetListIconLineColors(Gadget, Color1.l = #White, Color2.l = $CCCCCC)
  Protected iResult, iClear, w, h, rt.RECT, iCount, iPageItems, i, ImgTmp
  
  If GadgetType(Gadget) <> #PB_GadgetType_ListIcon : ProcedureReturn 0 : EndIf
  
  iCount = CountGadgetItems(Gadget)
  
  If iCount = 0
    AddGadgetItem(Gadget, -1, "test")
    iClear = 1
  EndIf
  
  w = GadgetWidth(Gadget)
  If w = 0 : w = 100 : EndIf
  
  rt\left = #LVIR_BOUNDS
  SendMessage_(GadgetID(Gadget), #LVM_GETITEMRECT, 0, rt)
  h = rt\bottom - rt\top
  
  iPageItems = SendMessage_(GadgetID(Gadget), #LVM_GETCOUNTPERPAGE, 0, 0)
  
  If iCount > iPageItems
    If ImgListIconBk0 : FreeImage(ImgListIconBk0) : EndIf
    ImgListIconBk0 = CreateImage(#PB_Any, w, h * 2)
    If ImgListIconBk0
      If StartDrawing(ImageOutput(ImgListIconBk0))
        Box(0, 0, w, h * 2, Color1)
        Box(0, h, w, h * 2, Color2)
        StopDrawing()
      EndIf
    EndIf
    
    If ImgListIconBk1 : FreeImage(ImgListIconBk1) : EndIf
    ImgListIconBk1 = CreateImage(#PB_Any, w, h * 2)
    If ImgListIconBk1
      If StartDrawing(ImageOutput(ImgListIconBk1))
        Box(0, 0, w, h * 2, Color2)
        Box(0, h, w, h * 2, Color1)
        StopDrawing()
      EndIf
    EndIf
    
    iResult = SetListIconBkImage(Gadget, ImgListIconBk0)
    
  Else
    ImgTmp = CreateImage(#PB_Any, w, h * iPageItems, 24, Color1)
    If ImgTmp
      iCount - 2
      If StartDrawing(ImageOutput(ImgTmp))
        For i = 0 To iCount Step 2
          Box(0, h * i, w, h, Color1)
          Box(0, h * (i + 1), w, h, Color2)
        Next
        StopDrawing()
        iResult = SetListIconBkImage(Gadget, ImgTmp)
      EndIf
      FreeImage(ImgTmp)
    EndIf
    
  EndIf
  
  If iClear : ClearGadgetItems(Gadget) : EndIf
  
  ProcedureReturn iResult
EndProcedure

Procedure WndProc_ListIcon(hWnd, Message, wParam, lParam)
  Protected iIdxFirst, iPageItems, img
  
  If Message = #WM_PAINT
    iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
    iPageItems = SendMessage_(GadgetID(ListIcon), #LVM_GETCOUNTPERPAGE, 0, 0)
    SendMessage_(GadgetID(ListIcon), #LVM_REDRAWITEMS, iIdxFirst, iIdxFirst + iPageItems)
  EndIf
  
  If Message = #WM_KEYUP ;Or Message = #WM_KEYDOWN
    iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
    If iIdxFirst % 2
      img = ImgListIconBk1
    Else
      img = ImgListIconBk0
    EndIf
    SetListIconBkImage(ListIcon, img)
  EndIf
  
  ProcedureReturn CallWindowProc_(OldWndProc, hWnd, Message, wParam, lParam)
EndProcedure

Procedure WndProc(hWnd, Message, wParam, lParam)
  Protected lvi.LVBKIMAGE, img, iIdxFirst, iPageItems, *lparam.NMHDR
  
  If Message = #WM_NOTIFY
    If lParam
      *lparam = lParam
      If *lparam\hwndFrom = GadgetID(ListIcon)
        
        iIdxFirst = SendMessage_(*lparam\hwndFrom, #LVM_GETTOPINDEX, 0, 0)
        
        Select *lparam\code
          ;Case #LVN_BEGINSCROLL
          Case #LVN_ENDSCROLL, #LVN_KEYDOWN
            If iIdxFirst % 2
              img = ImgListIconBk1
            Else
              img = ImgListIconBk0
            EndIf
            SetListIconBkImage(ListIcon, img)
            
          Case #LVN_ITEMCHANGING, #NM_SETFOCUS
            iPageItems = SendMessage_(GadgetID(ListIcon), #LVM_GETCOUNTPERPAGE, 0, 0)
            SendMessage_(GadgetID(ListIcon), #LVM_REDRAWITEMS, iIdxFirst, iIdxFirst + iPageItems)
            
        EndSelect
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIcon = ListIconGadget(#PB_Any, 34, 30, 388, 402, "Col 1", 100)

AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)

Define ImgList = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)

;For i = 0 To 10
For i = 0 To 100
	AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
Next

icon_index = Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 5, 2, icon_index)
ListIcon_SetItemImage(ListIcon, 6, 2, icon_index)

SetListIconLineColors(ListIcon)

; For fixing arrow up/down key bug.
OldWndProc = SetWindowLongPtr_(GadgetID(ListIcon), #GWLP_WNDPROC, @WndProc_ListIcon())

SetWindowCallback(@WndProc())

Repeat
  e = WaitWindowEvent()
Until e = #PB_Event_CloseWindow
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: ListIcon and image transparency

Post by tatanas »

Great code breeze4me !

The problem is that I'm using #PB_ListIcon_FullRowSelect style and the image background stays grey or white instead of the highlight color when selected. I tryed adding a SetListIconLineColorsHighLight() function with GetSysColor_(#COLOR_HIGHLIGHT) as background color and then apply it on #NM_CLICK but all items background are getting colored...
Windows 10 Pro x64
PureBasic 6.20 x64
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4953
Joined: Sun Apr 12, 2009 6:27 am

Re: ListIcon and image transparency

Post by RASHAD »

# 1:

Code: Select all

Global Selbrush,Defbrush
Global icoHnd

Selbrush  = CreateSolidBrush_($6163FE);($FF9933)
Defbrush = CreateSolidBrush_($D4D4D4)

Procedure CreateIcon( DLL_Path.s, index)
  Protected hIcon, img_index = -1  
  iinf.ICONINFO
  hIcon = ExtractIcon_(#Null, DLL_Path, index)  
  StartVectorDrawing(ImageVectorOutput(0))
  DrawVectorImage(hicon)
  StopVectorDrawing()
  iinf\hbmMask = ImageID(0)
  iinf\hbmColor = ImageID(0)
  icoHnd = CreateIconIndirect_(iinf)  
  DestroyIcon_(hIcon)
EndProcedure

Procedure WinCallback(hWnd, uMsg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents 
  Select uMsg
    Case #WM_NOTIFY
      *nmhdr.NMHDR = lParam
      *lvCD.NMLVCUSTOMDRAW = lParam
      gHnd = *lvCD\nmcd\hdr\hwndFrom
      If gHnd = GadgetID(0) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW 
        gDC = *lvCD\nmcd\hdc  
        Select *lvCD\nmcd\dwDrawStage
          Case #CDDS_PREPAINT
            result = #CDRF_NOTIFYITEMDRAW
          Case #CDDS_ITEMPREPAINT
            result = #CDRF_NOTIFYSUBITEMDRAW
          Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM              
            *lvCD\clrTextBk = $D4D4D4 
            *lvCD\clrText = $B30059 
            RowNumber             = *lvCD\nmcd\dwItemSpec
            ColumnNumber          = *lvCD\iSubItem
            If RowNumber >= 0
              subItemRect.RECT\left = #LVIR_LABEL
              subItemRect.RECT\top  = *lvCD\iSubItem    
              SendMessage_(gHnd, #LVM_GETSUBITEMRECT, RowNumber, @subItemRect)              
              TextString1.s = GetGadgetItemText(0, RowNumber, ColumnNumber)
              TextString2.s = GetGadgetItemText(0, RowNumber, ColumnNumber)              
              r.RECT 
              r\top = *lvCD\iSubItem 
              r\left = #LVIR_BOUNDS 
              SendMessage_(gHnd, #LVM_GETSUBITEMRECT, RowNumber, r)
              If RowNumber % 2 = 1
                FillRect_(gDC, subItemRect, Defbrush)
                SetBkColor_(gDC, $D4D4D4 )  
              EndIf                 
              If GetGadgetState(0) = RowNumber 
                FillRect_(gDC, subItemRect, selbrush) 
                DrawIconEx_(gDC,4,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	)              
                SetTextColor_(gDC, #Black )
                If *lvCD\iSubItem = 0
                  r\left + 26                  
                  DrawText_(gDC, TextString1.s, Len(TextString1.s), r, #DT_VCENTER)
                  DrawIconEx_(gDC,4,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	)                   
                ElseIf *lvCD\iSubItem = 1
                  r\left + 5                  
                  DrawText_(gDC, TextString1.s, Len(TextString2.s), r, #DT_VCENTER)
                  DrawIconEx_(gDC,r\left+90,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	)
                EndIf
              Else
                If *lvCD\iSubItem = 0
                  r\left + 26
                  DrawText_(gDC, TextString1.s, Len(TextString1.s), r, #DT_VCENTER)
                  DrawIconEx_(gDC,4,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	) 
                ElseIf *lvCD\iSubItem = 1
                  r\left + 5                  
                  DrawText_(gDC, TextString1.s, Len(TextString2.s), r, #DT_VCENTER)
                  DrawIconEx_(gDC,r\left+90,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	)
                EndIf 
              EndIf               
              result = #CDRF_SKIPDEFAULT         
            EndIf
        EndSelect
      EndIf  
  EndSelect
  ProcedureReturn result
EndProcedure

LoadFont(0,"Comic Sans MS", 12, 0)
LoadFont(1,"Georgia", 14, 0)
CreateImage(0,24,24,32,#PB_Image_Transparent )
OpenWindow(0, 0, 0, 464, 462, "Different Colors per Item", #PB_Window_SystemMenu| #PB_Window_MinimizeGadget| #PB_Window_ScreenCentered)
SetWindowCallback(@WinCallback())
ListIconGadget(0, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection )
AddGadgetColumn(0, 1, "Col 2", 120)
AddGadgetColumn(0, 2, "Col 3", 100)

SetGadgetFont(0,FontID(1))

For i = 0 To 10
  AddGadgetItem(0, i, "item" + i + Chr(10) + "subitem" + i)
Next

CreateIcon( "shell32.dll", 296)    
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Quit = 1      
      
  EndSelect
Until Quit = 1
DeleteObject_(Selbrush)
DeleteObject_(Defbrush)
End 
# 2: Much better optimized code

Code: Select all

Global Selbrush,Defbrush
Global icoHnd

Selbrush  = CreateSolidBrush_($6163FE);($FF9933)
Defbrush = CreateSolidBrush_($D4D4D4)

Procedure CreateIcon( DLL_Path.s, index)
  Protected hIcon, img_index = -1  
  iinf.ICONINFO
  hIcon = ExtractIcon_(#Null, DLL_Path, index)  
  StartVectorDrawing(ImageVectorOutput(0))
  DrawVectorImage(hicon)
  StopVectorDrawing()
  iinf\hbmMask = ImageID(0)
  iinf\hbmColor = ImageID(0)
  icoHnd = CreateIconIndirect_(iinf)  
  DestroyIcon_(hIcon)
EndProcedure

Procedure WinCallback(hWnd, uMsg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents 
  Select uMsg
    Case #WM_NOTIFY
      *nmhdr.NMHDR = lParam
      *lvCD.NMLVCUSTOMDRAW = lParam
      gHnd = *lvCD\nmcd\hdr\hwndFrom
      If gHnd = GadgetID(0) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW 
        gDC = *lvCD\nmcd\hdc  
        Select *lvCD\nmcd\dwDrawStage
          Case #CDDS_PREPAINT
            result = #CDRF_NOTIFYITEMDRAW
          Case #CDDS_ITEMPREPAINT
            result = #CDRF_NOTIFYSUBITEMDRAW
          Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM              
            *lvCD\clrTextBk = $D4D4D4 
            *lvCD\clrText = $B30059 
            RowNumber             = *lvCD\nmcd\dwItemSpec
            ColumnNumber          = *lvCD\iSubItem
            If RowNumber >= 0
              subItemRect.RECT\left = #LVIR_LABEL
              subItemRect.RECT\top  = *lvCD\iSubItem    
              SendMessage_(gHnd, #LVM_GETSUBITEMRECT, RowNumber, @subItemRect)              
              TextString1.s = GetGadgetItemText(0, RowNumber, ColumnNumber)
              TextString2.s = GetGadgetItemText(0, RowNumber, ColumnNumber)              
              r.RECT 
              r\top = *lvCD\iSubItem 
              r\left = #LVIR_BOUNDS 
              SendMessage_(gHnd, #LVM_GETSUBITEMRECT, RowNumber, r)
              If RowNumber % 2 = 1
                FillRect_(gDC, subItemRect, Defbrush)
                SetBkColor_(gDC, $D4D4D4 )  
              EndIf
              If GetGadgetState(0) = RowNumber 
                FillRect_(gDC, subItemRect, selbrush)
              EndIf  
              If *lvCD\iSubItem = 0
                r\left + 26
                DrawText_(gDC, TextString1.s, Len(TextString1.s), r, #DT_VCENTER)
                DrawIconEx_(gDC,4,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	) 
              ElseIf *lvCD\iSubItem = 1
                r\left + 5                  
                DrawText_(gDC, TextString1.s, Len(TextString2.s), r, #DT_VCENTER)
                DrawIconEx_(gDC,r\left+90,subItemRect\top+4,icoHnd,16,16,0,0,#DI_NORMAL	)
              EndIf 
              result = #CDRF_SKIPDEFAULT         
            EndIf
        EndSelect
      EndIf  
  EndSelect
  ProcedureReturn result
EndProcedure

LoadFont(0,"Comic Sans MS", 12, 0)
LoadFont(1,"Georgia", 14, 0)
CreateImage(0,24,24,32,#PB_Image_Transparent )
OpenWindow(0, 0, 0, 464, 462, "Different Colors per Item", #PB_Window_SystemMenu| #PB_Window_MinimizeGadget| #PB_Window_ScreenCentered)
SetWindowCallback(@WinCallback())
ListIconGadget(0, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection )
AddGadgetColumn(0, 1, "Col 2", 120)
AddGadgetColumn(0, 2, "Col 3", 100)

SetGadgetFont(0,FontID(1))

For i = 0 To 10
  AddGadgetItem(0, i, "item" + i + Chr(10) + "subitem" + i)
Next

CreateIcon( "shell32.dll", 296)    
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Quit = 1      
      
  EndSelect
Until Quit = 1
DeleteObject_(Selbrush)
DeleteObject_(Defbrush)
End 
Last edited by RASHAD on Wed Apr 03, 2024 6:20 pm, edited 1 time in total.
Egypt my love
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: ListIcon and image transparency

Post by breeze4me »

In practice, the custom drawing is shorter if you do a few calculations like RASHAD's code. :wink:
Fixed some glitches in the previous code.

Code: Select all

Import "Msimg32.lib"
  TransparentBlt(hdcDest, xoriginDest.l, yoriginDest.l, wDest.l, hDest.l, hdcSrc, xoriginSrc.l, yoriginSrc.l, wSrc.l, hSrc.l, crTransparent.l)
EndImport


Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
	Protected hIcon, img_index = -1
 	hIcon = ExtractIcon_(#Null, DLL_Path, index)
	If hIcon
		img_index = ImageList_AddIcon_(ImgList, hIcon)
		DestroyIcon_(hIcon)
	Else
		ProcedureReturn -1
	EndIf
	ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
	Protected itemLV.LVITEM ; tagLVITEM
	itemLV\mask = #LVIF_IMAGE
	itemLV\iItem = index_item
	itemLV\iSubItem = index_subitem
	itemLV\iImage = index_image
	If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
		Debug "#LVM_SETITEM error"
	EndIf
EndProcedure



Structure LVBKIMAGE Align #PB_Structure_AlignC
  ulFlags.l
  hbm.i
  pszImage.s
  cchImageMax.l
  xOffsetPercent.l
  yOffsetPercent.l 
EndStructure

#LVBKIF_SOURCE_NONE = 0
#LVBKIF_SOURCE_HBITMAP = 1
#LVBKIF_STYLE_TILE = 16

Global ListIcon
Global ImgListIconBk0, ImgListIconBk1, ListIconBkColor0.l, ListIconBkColor1.l
Global KeyPressed
Global OldWndProc

Procedure SetListIconBkImage(Gadget, Image)
  Protected iResult, hdc, hdcDst, hBitmap, w, h, lvi.LVBKIMAGE, old
  
  If Image
    w = ImageWidth(Image)
    h = ImageHeight(Image)
    If w = 0 Or h = 0 : ProcedureReturn 0 :  EndIf
    
    ; https://learn.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-lvbkimagew
    ; #LVBKIF_SOURCE_HBITMAP
    ; "A background bitmap is supplied via the hbm member of LVBKIMAGE. If the message LVM_SETBKIMAGE succeeds, then the list-view takes ownership of the bitmap."
    
    ; The ListIconGadget takes ownership of the image, so you need to create a new bitmap and pass it over.
    ; The bitmap is internally destroyed when the image is replaced.
    hdc = StartDrawing(ImageOutput(Image))
    If hdc
      hdcDst = CreateCompatibleDC_(hdc)
      If hdcDst
        hBitmap = CreateCompatibleBitmap_(hdc, w, h)
        If hBitmap
          old = SelectObject_(hdcDst, hBitmap)
          BitBlt_(hdcDst, 0, 0, w, h, hdc, 0, 0, #SRCCOPY)
          SelectObject_(hdcDst, old)
          
          lvi\ulFlags = #LVBKIF_SOURCE_HBITMAP | #LVBKIF_STYLE_TILE
          lvi\hbm = hBitmap
          iResult = SendMessage_(GadgetID(Gadget), #LVM_SETBKIMAGE, 0, lvi)
        EndIf
        DeleteDC_(hdcDst)
      EndIf
      StopDrawing()
    EndIf
  EndIf
  ProcedureReturn iResult
EndProcedure

Procedure SetListIconLineColors(Gadget, Color1.l = #White, Color2.l = $CCCCCC)
  Protected iResult, iClear, w, h, rt.RECT, iCount, iPageItems, i, ImgTmp
  
  If GadgetType(Gadget) <> #PB_GadgetType_ListIcon : ProcedureReturn 0 : EndIf
  
  If GetSysColor_(#COLOR_HIGHLIGHTTEXT) = Color1
    Color1 - 1
  EndIf
  
  iCount = CountGadgetItems(Gadget)
  
  If iCount = 0
    AddGadgetItem(Gadget, -1, "test")
    iClear = 1
  EndIf
  
  w = GadgetWidth(Gadget)
  If w = 0 : w = 100 : EndIf
  
  rt\left = #LVIR_BOUNDS
  SendMessage_(GadgetID(Gadget), #LVM_GETITEMRECT, 0, rt)
  h = rt\bottom - rt\top
  
  iPageItems = SendMessage_(GadgetID(Gadget), #LVM_GETCOUNTPERPAGE, 0, 0)
  
  If iCount > iPageItems
    If ImgListIconBk0 : FreeImage(ImgListIconBk0) : EndIf
    ImgListIconBk0 = CreateImage(#PB_Any, w, h * 2)
    If ImgListIconBk0
      If StartDrawing(ImageOutput(ImgListIconBk0))
        Box(0, 0, w, h * 2, Color1)
        Box(0, h, w, h * 2, Color2)
        StopDrawing()
      EndIf
    EndIf
    
    If ImgListIconBk1 : FreeImage(ImgListIconBk1) : EndIf
    ImgListIconBk1 = CreateImage(#PB_Any, w, h * 2)
    If ImgListIconBk1
      If StartDrawing(ImageOutput(ImgListIconBk1))
        Box(0, 0, w, h * 2, Color2)
        Box(0, h, w, h * 2, Color1)
        StopDrawing()
      EndIf
    EndIf
    
    iResult = SetListIconBkImage(Gadget, ImgListIconBk0)
    
  Else
    ImgTmp = CreateImage(#PB_Any, w, h * iPageItems, 24, Color1)
    If ImgTmp
      iCount - 2
      If StartDrawing(ImageOutput(ImgTmp))
        For i = 0 To iCount Step 2
          Box(0, h * i, w, h, Color1)
          Box(0, h * (i + 1), w, h, Color2)
        Next
        StopDrawing()
        iResult = SetListIconBkImage(Gadget, ImgTmp)
      EndIf
      FreeImage(ImgTmp)
    EndIf
    
  EndIf
  
  If iResult
    ListIconBkColor0 = color1
    ListIconBkColor1 = color2
  EndIf
  
  If iClear : ClearGadgetItems(Gadget) : EndIf
  
  ProcedureReturn iResult
EndProcedure

Procedure WndProc_ListIcon(hWnd, Message, wParam, lParam)
  Protected iIdxFirst, iPageItems, img
  
  Select Message
    Case #WM_SETFOCUS
      SendMessage_(hWnd, #WM_CHANGEUISTATE, #UIS_CLEAR | (#UISF_HIDEFOCUS << 16), 0)
      
    Case #WM_PAINT, #WM_KILLFOCUS
      iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
      iPageItems = SendMessage_(GadgetID(ListIcon), #LVM_GETCOUNTPERPAGE, 0, 0)
      SendMessage_(GadgetID(ListIcon), #LVM_REDRAWITEMS, iIdxFirst, iIdxFirst + iPageItems)
      
    Case #WM_KEYDOWN
      Select wParam
        Case #VK_UP, #VK_DOWN, #VK_PRIOR, #VK_NEXT, #VK_HOME, #VK_END
          KeyPressed = 1
      EndSelect
      
    Case #WM_KEYUP
      KeyPressed = 0
      
      iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
      If iIdxFirst % 2
        img = ImgListIconBk1
      Else
        img = ImgListIconBk0
      EndIf
      SetListIconBkImage(ListIcon, img)
      
  EndSelect
  ProcedureReturn CallWindowProc_(OldWndProc, hWnd, Message, wParam, lParam)
EndProcedure

Procedure WndProc(hWnd, Message, wParam, lParam)
  Protected iResult = #PB_ProcessPureBasicEvents
  Protected img, iIdxFirst, iPageItems, *lparam.NMHDR, *lvcd.NMLVCUSTOMDRAW, iSetBk, rt.RECT, rtIcon.RECT
  Protected w, h, BkColor, ItemSelected, hdcMem, hBitmap, old, hBrush
  
  If Message = #WM_NOTIFY
    If lParam
      *lparam = lParam
      If *lparam\hwndFrom = GadgetID(ListIcon)
        
        iIdxFirst = SendMessage_(*lparam\hwndFrom, #LVM_GETTOPINDEX, 0, 0)
        
        Select *lparam\code
          Case #NM_CUSTOMDRAW
            *lvcd = lParam
            Select *lvcd\nmcd\dwDrawStage
              Case #CDDS_ITEMPREPAINT
                ProcedureReturn #CDRF_NOTIFYPOSTPAINT
                
              Case #CDDS_ITEMPOSTPAINT
                ItemSelected = Bool(GetGadgetItemState(ListIcon, *lvcd\nmcd\dwItemSpec) & #PB_ListIcon_Selected)
                If ItemSelected = 0 : ProcedureReturn #PB_ProcessPureBasicEvents : EndIf
                If GetWindowLongPtr_(GadgetID(ListIcon), #GWL_STYLE) & #LVS_SHOWSELALWAYS <> #LVS_SHOWSELALWAYS And GetActiveGadget() <> ListIcon
                  ProcedureReturn #PB_ProcessPureBasicEvents
                EndIf
                
                rt\left = #LVIR_BOUNDS
                SendMessage_(GadgetID(ListIcon), #LVM_GETITEMRECT, *lvcd\nmcd\dwItemSpec, rt)
                rtIcon\left = #LVIR_ICON
                SendMessage_(GadgetID(ListIcon), #LVM_GETITEMRECT, *lvcd\nmcd\dwItemSpec, rtIcon)
                
                rt\left + rtIcon\right
                w = rt\right - rt\left
                h = rt\bottom - rt\top
                
                If ItemSelected And GetActiveGadget() = ListIcon
                  BkColor = GetSysColor_(#COLOR_HIGHLIGHT)
                Else
                  BkColor = GetSysColor_(#COLOR_BTNFACE)
                EndIf
                
                hdcMem = CreateCompatibleDC_(*lvcd\nmcd\hdc)
                If hdcMem
                  hBitmap = CreateCompatibleBitmap_(*lvcd\nmcd\hdc, w, h)
                  If hBitmap
                    old = SelectObject_(hdcMem, hBitmap)
                    
                    If *lvcd\nmcd\uItemState & #CDIS_FOCUS
                      DrawFocusRect_(*lvcd\nmcd\hdc, rt)
                    EndIf
                    BitBlt_(hdcMem, 0, 0, w, h, *lvcd\nmcd\hdc, rt\left, rt\top, #SRCCOPY)
                    
                    hBrush = CreateSolidBrush_(BkColor)
                    If hBrush
                      FillRect_(*lvcd\nmcd\hdc, rt, hBrush)
                      DeleteObject_(hBrush)
                    EndIf
                    
                    If *lvcd\nmcd\dwItemSpec % 2
                      TransparentBlt(*lvcd\nmcd\hdc, rt\left, rt\top, w, h, hdcMem, 0, 0, w, h, ListIconBkColor1)
                    Else
                      TransparentBlt(*lvcd\nmcd\hdc, rt\left, rt\top, w, h, hdcMem, 0, 0, w, h, ListIconBkColor0)
                    EndIf
                    
                    If *lvcd\nmcd\uItemState & #CDIS_FOCUS
                      DrawFocusRect_(*lvcd\nmcd\hdc, rt)
                    EndIf
                    
                    SelectObject_(hdcMem, old)
                    DeleteObject_(hBitmap)
                  EndIf
                  DeleteDC_(hdcMem)
                EndIf
                
            EndSelect
            
            If KeyPressed And *lvcd\nmcd\uItemState & #CDIS_FOCUS
              iSetBk = 1
            Else
              ProcedureReturn iResult
            EndIf
            
          Case #LVN_ENDSCROLL
            iSetBk = 1
            
          Case #LVN_ITEMCHANGING, #NM_SETFOCUS
            iPageItems = SendMessage_(GadgetID(ListIcon), #LVM_GETCOUNTPERPAGE, 0, 0)
            SendMessage_(GadgetID(ListIcon), #LVM_REDRAWITEMS, iIdxFirst, iIdxFirst + iPageItems)
            
        EndSelect
        
        If iSetBk
          If iIdxFirst % 2
            img = ImgListIconBk1
          Else
            img = ImgListIconBk0
          EndIf
          SetListIconBkImage(ListIcon, img)
        EndIf
        
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn iResult
EndProcedure


Define Window = OpenWindow(#PB_Any, 0, 0, 464, 462, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIcon = ListIconGadget(#PB_Any, 34, 30, 388, 402, "Col 1", 100, #PB_ListIcon_FullRowSelect); | #PB_ListIcon_AlwaysShowSelection); | #PB_ListIcon_MultiSelect)

AddGadgetColumn(ListIcon, 1, "Col 2", 100)
AddGadgetColumn(ListIcon, 2, "Col 3", 100)

Define ImgList = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 10)
SendMessage_(GadgetID(ListIcon), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
SendMessage_(GadgetID(ListIcon), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)


;For i = 0 To 10
For i = 0 To 100
	AddGadgetItem(ListIcon, i, "item" + i + Chr(10) + "subitem" + i)
Next

icon_index = Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
ListIcon_SetItemImage(ListIcon, 5, 2, icon_index)
ListIcon_SetItemImage(ListIcon, 6, 2, icon_index)

SetListIconLineColors(ListIcon)

; For fixing arrow up/down key bug.
OldWndProc = SetWindowLongPtr_(GadgetID(ListIcon), #GWLP_WNDPROC, @WndProc_ListIcon())

SetWindowCallback(@WndProc())

Repeat
  e = WaitWindowEvent()
Until e = #PB_Event_CloseWindow

If you want to change the icon background for the first column as well, modify it like this.

Code: Select all

                ......
                rt\left = #LVIR_BOUNDS
                SendMessage_(GadgetID(ListIcon), #LVM_GETITEMRECT, *lvcd\nmcd\dwItemSpec, rt)
                rtIcon\left = #LVIR_ICON
                SendMessage_(GadgetID(ListIcon), #LVM_GETITEMRECT, *lvcd\nmcd\dwItemSpec, rtIcon)
                
                w = rt\right - rt\left
                h = rt\bottom - rt\top
                
                If ItemSelected And GetActiveGadget() = ListIcon
                  BkColor = GetSysColor_(#COLOR_HIGHLIGHT)
                Else
                  BkColor = GetSysColor_(#COLOR_BTNFACE)
                EndIf
                
                hdcMem = CreateCompatibleDC_(*lvcd\nmcd\hdc)
                If hdcMem
                  hBitmap = CreateCompatibleBitmap_(*lvcd\nmcd\hdc, w, h)
                  If hBitmap
                    old = SelectObject_(hdcMem, hBitmap)
                    
                    If *lvcd\nmcd\uItemState & #CDIS_FOCUS
                      rt\left + rtIcon\right
                      DrawFocusRect_(*lvcd\nmcd\hdc, rt)
                      rt\left - rtIcon\right
                    EndIf
                    BitBlt_(hdcMem, 0, 0, w, h, *lvcd\nmcd\hdc, rt\left, rt\top, #SRCCOPY)
                    
                    hBrush = CreateSolidBrush_(BkColor)
                    If hBrush
                      FillRect_(*lvcd\nmcd\hdc, rt, hBrush)
                      DeleteObject_(hBrush)
                    EndIf
                    
                    If *lvcd\nmcd\dwItemSpec % 2
                      TransparentBlt(*lvcd\nmcd\hdc, rt\left, rt\top, w, h, hdcMem, 0, 0, w, h, ListIconBkColor1)
                    Else
                      TransparentBlt(*lvcd\nmcd\hdc, rt\left, rt\top, w, h, hdcMem, 0, 0, w, h, ListIconBkColor0)
                    EndIf
                    
                    If *lvcd\nmcd\uItemState & #CDIS_FOCUS
                      DrawFocusRect_(*lvcd\nmcd\hdc, rt)
                    EndIf
                    
                    SelectObject_(hdcMem, old)
                    DeleteObject_(hBitmap)
                  EndIf
                  DeleteDC_(hdcMem)
                EndIf
                ......
Edit: Improved code.
Edit2: The code is isolated as much as possible to make it easy to apply. It's easy to modularize, if needed.

Code: Select all

EnableExplicit

#LVBKIF_SOURCE_HBITMAP = 1
#LVBKIF_STYLE_TILE = 16

#OldWndProc$ = "OldWndProc"
#LVBkData$ = "LVBkData"

Structure LVBKIMAGE Align #PB_Structure_AlignC
  ulFlags.l
  hbm.i
  pszImage.s
  cchImageMax.l
  xOffsetPercent.l
  yOffsetPercent.l
EndStructure

Structure STRUC_ListIconBkData
  Gadget.i
  hHeader.i
  iKeyPressed.i
  hBrush.i[2]
  lColor0.l
  lColor1.l
EndStructure

Procedure UpdateListIconBkImage(Gadget)
  Protected iResult
  Protected iIdxFirst, iPageItems, iItemCount, ItemW, ItemH, AllItemH, hWnd, hBrush, i
  Protected hBrushHL, hBrushBk, hdc, hdcDst, hBitmap, w, h, oldBitmap, oldBrush
  Protected rt.RECT, lvi.LVBKIMAGE, lHighlightColor.l = -1
  Protected *ListData.STRUC_ListIconBkData
  
  hWnd = GadgetID(Gadget)
  *ListData = GetProp_(hWnd, #LVBkData$)
  If *ListData = 0 : ProcedureReturn 0 : EndIf
  
  GetClientRect_(hWnd, rt)
  w = rt\right
  h = rt\bottom
  
  iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
  iItemCount = SendMessage_(hWnd, #LVM_GETITEMCOUNT, 0, 0)
  iPageItems = SendMessage_(hWnd, #LVM_GETCOUNTPERPAGE, 0, 0)
  
  rt\left = #LVIR_BOUNDS
  SendMessage_(hWnd, #LVM_GETITEMRECT, 0, rt)
  ItemW = rt\right - rt\left
  ItemH = rt\bottom - rt\top
  
  AllItemH = iItemCount * ItemH
  
  If GetActiveGadget() = Gadget
    lHighlightColor = GetSysColor_(#COLOR_HIGHLIGHT)
  ElseIf GetWindowLongPtr_(hWnd, #GWL_STYLE) & #LVS_SHOWSELALWAYS ;= #LVS_SHOWSELALWAYS
    lHighlightColor = GetSysColor_(#COLOR_BTNFACE)
  EndIf
  
  If lHighlightColor <> -1
    hBrushHL = CreateSolidBrush_(lHighlightColor)
  EndIf
  
  hBrush = *ListData\hBrush[iIdxFirst % 2]
  
  hdc = GetDC_(hWnd)
  If hdc
    hdcDst = CreateCompatibleDC_(hdc)
    If hdcDst
      hBitmap = CreateCompatibleBitmap_(hdc, w, h)
      If hBitmap
        oldBitmap = SelectObject_(hdcDst, hBitmap)
        
        ; If the client area height is greater than the sum of all item heights,
        ; fill the client area with the default background color.
        If AllItemH < h
          hBrushBk = CreateSolidBrush_(GetSysColor_(#COLOR_WINDOW))
          If hBrushBk
            oldBrush = SelectObject_(hdcDst, hBrushBk)
            PatBlt_(hdcDst, 0, 0, w, h, #PATCOPY)
            SelectObject_(hdcDst, oldBrush)
            DeleteObject_(hBrushBk)
          EndIf
          h = AllItemH
        EndIf
        
        ; Fill only up to the area of the visible items with the brush.
        oldBrush = SelectObject_(hdcDst, hBrush)
        PatBlt_(hdcDst, 0, 0, w, h, #PATCOPY)
        
        If hBrushHL
          SelectObject_(hdcDst, hBrushHL)
          
          ; If the total number of items is less than what can be shown on the current page, only checks the status of visible items.
          If iItemCount < iPageItems
            iPageItems = iItemCount
          EndIf
          
          ; Fills the selected item position with a highlight brush.
          For i = 0 To iPageItems
            If SendMessage_(hWnd, #LVM_GETITEMSTATE, iIdxFirst + i, #LVIS_SELECTED)
              PatBlt_(hdcDst, rt\left, i * ItemH, ItemW, ItemH, #PATCOPY)
            EndIf
          Next
        EndIf
        
        SelectObject_(hdcDst, oldBrush)
        SelectObject_(hdcDst, oldBitmap)
        
        lvi\ulFlags = #LVBKIF_SOURCE_HBITMAP | #LVBKIF_STYLE_TILE
        lvi\hbm = hBitmap
        iResult = SendMessage_(hWnd, #LVM_SETBKIMAGE, 0, lvi)
      EndIf
      DeleteDC_(hdcDst)
    EndIf
    ReleaseDC_(hWnd, hdc)
  EndIf
  
  If hBrushHL : DeleteObject_(hBrushHL) : EndIf
  
  ProcedureReturn iResult
EndProcedure

; Color1, Color2: RGB Color value
;                 -1   Keep existing color, or default color(the first time this function is executed)
Procedure SetListIconLineColors(Gadget, Color1.l = #White, Color2.l = $CCCCCC)
  Protected iClear, h, rt.RECT, img, hHeader, hWnd, i, *ListData.STRUC_ListIconBkData
  Protected Dim Color.l(1)
  
  If GadgetType(Gadget) <> #PB_GadgetType_ListIcon : ProcedureReturn 0 : EndIf
  hWnd = GadgetID(Gadget)
  *ListData = GetProp_(hWnd, #LVBkData$)
  If *ListData = 0
    *ListData = AllocateMemory(SizeOf(STRUC_ListIconBkData))
    If *ListData
      SetProp_(hWnd, #LVBkData$, *ListData)
      *ListData\Gadget = Gadget
      hHeader = SendMessage_(hWnd, #LVM_GETHEADER, 0, 0)
      If hHeader
        *ListData\hHeader = hHeader
        SetWindowLongPtr_(hHeader, #GWLP_ID, Gadget)
      EndIf
      If Color1 = -1
        *ListData\lColor0 = #White
      Else
        *ListData\lColor0 = Color1
      EndIf
      If Color2 = -1
        *ListData\lColor1 = $CCCCCC
      Else
        *ListData\lColor1 = Color2
      EndIf
    EndIf
  EndIf
  If *ListData = 0 : ProcedureReturn 0 : EndIf
  
  If Color1 = -1
    Color(0) = *ListData\lColor0
  Else
    Color(0) = Color1
  EndIf
  If Color2 = -1
    Color(1) = *ListData\lColor1
  Else
    Color(1) = Color2
  EndIf
  
  If CountGadgetItems(Gadget) = 0
    AddGadgetItem(Gadget, -1, "test")
    iClear = 1
  EndIf
  
  rt\left = #LVIR_BOUNDS
  SendMessage_(hWnd, #LVM_GETITEMRECT, 0, rt)
  h = rt\bottom - rt\top
  
  For i = 0 To 1
    img = CreateImage(#PB_Any, 200, h * 2, 24, Color(i))
    If img
      If StartDrawing(ImageOutput(img))
        Box(0, h, 200, h, Color(i ! 1))
        StopDrawing()
        If *ListData\hBrush[i] : DeleteObject_(*ListData\hBrush[i]) : EndIf
        *ListData\hBrush[i] = CreatePatternBrush_(ImageID(img))
      EndIf
      FreeImage(img)
    EndIf
  Next
  
  If iClear : ClearGadgetItems(Gadget) : EndIf
  
  ProcedureReturn UpdateListIconBkImage(Gadget)
EndProcedure

;Note: The gadget number must not be zero.
Procedure WndProc_ListIcon(hWnd, Message, wParam, lParam)
  Protected OldWndProc = GetProp_(hWnd, #OldWndProc$)
  Protected iIdxFirst, iPageItems, *ListData.STRUC_ListIconBkData
  
  Select Message
    Case #WM_PAINT, #WM_KILLFOCUS
      iIdxFirst = SendMessage_(hWnd, #LVM_GETTOPINDEX, 0, 0)
      iPageItems = SendMessage_(hWnd, #LVM_GETCOUNTPERPAGE, 0, 0)
      SendMessage_(hWnd, #LVM_REDRAWITEMS, iIdxFirst, iIdxFirst + iPageItems)
      
    Case #WM_KEYDOWN
      Select wParam
        Case #VK_UP, #VK_DOWN, #VK_PRIOR, #VK_NEXT, #VK_HOME, #VK_END
          *ListData = GetProp_(hWnd, #LVBkData$)
          If *ListData
            *ListData\iKeyPressed = 1
          EndIf
      EndSelect
      
    Case #WM_KEYUP
      *ListData = GetProp_(hWnd, #LVBkData$)
      If *ListData
        *ListData\iKeyPressed = 0
        UpdateListIconBkImage(*ListData\Gadget)
      EndIf
      
    Case #WM_NCDESTROY
      *ListData = GetProp_(hWnd, #LVBkData$)
      If *ListData : FreeMemory(*ListData) : EndIf
      RemoveProp_(hWnd, #LVBkData$)
      RemoveProp_(hWnd, #OldWndProc$)
  EndSelect
  
  ProcedureReturn CallWindowProc_(OldWndProc, hWnd, Message, wParam, lParam)
EndProcedure

Procedure ProcessListIconNotification(hWnd, wParam, *lParam.NMHDR)
  Protected *lvcd.NMLVCUSTOMDRAW, *hdr.NMHEADER
  Protected Gadget, iSetBk, iColunmWidth, StopPaint, hListIcon
  Protected *ListData.STRUC_ListIconBkData
  
  Static iPrevColunmWidth
  
  If *lParam = 0 : ProcedureReturn 0 : EndIf
  
  Gadget = GetWindowLongPtr_(*lParam\hwndFrom, #GWLP_ID)
  If Gadget = 0 : ProcedureReturn 0 : EndIf
  
  hListIcon = GadgetID(Gadget)
  *ListData = GetProp_(hListIcon, #LVBkData$)
  If *ListData = 0 : ProcedureReturn 0 : EndIf
  
  If *lParam\hwndFrom = *ListData\hHeader
    ; Notification from a header control.
    ; To fix an issue with the icon background of subitems when resizing the headers.
    *hdr = *lParam
    Select *lParam\code
      Case #HDN_ENDTRACK
        StopPaint = 0
        iPrevColunmWidth = -1000
        RedrawWindow_(hListIcon, 0, 0, #RDW_INVALIDATE | #RDW_UPDATENOW)
        
      Case #HDN_BEGINTRACK
        iPrevColunmWidth = -1000
        
      Case #HDN_ITEMCHANGED
        iColunmWidth = GetGadgetItemAttribute(Gadget, 0, #PB_ListIcon_ColumnWidth, *hdr\iItem)
        If iPrevColunmWidth <> iColunmWidth
          iPrevColunmWidth = iColunmWidth
          StopPaint = 1
          iSetBk = 1
        EndIf
    EndSelect
    
  ElseIf *lParam\hwndFrom = hListIcon
    ; Notification from a ListIcon gadget.
    ; To fix issues when scrolling through gadget items.
    Select *lParam\code
      Case #NM_CUSTOMDRAW
        *lvcd = *lParam
        If (*ListData\iKeyPressed And *lvcd\nmcd\uItemState & #CDIS_FOCUS) ;Or GetGadgetItemState(Gadget, *lvcd\nmcd\dwItemSpec) & #PB_ListIcon_Selected
          iSetBk = 1
        Else
          ProcedureReturn 0
        EndIf
        
      Case #LVN_BEGINSCROLL;, #LVN_ITEMCHANGING
        LockWindowUpdate_(hListIcon)
        
      Case #LVN_ENDSCROLL, #NM_SETFOCUS, #LVN_ITEMCHANGED, #NM_KILLFOCUS ;,#LVN_ITEMCHANGING
        iSetBk = 1
    EndSelect
  EndIf
  
  If iSetBk
    UpdateListIconBkImage(Gadget)
    LockWindowUpdate_(0)
    
    ; To fix flickering when resizing the headers.
    If StopPaint
      ValidateRect_(hListIcon, 0)
    EndIf
  EndIf
  ProcedureReturn 1
EndProcedure



;- Example

Procedure WndProc(hWnd, Message, wParam, lParam)
  Protected iResult = #PB_ProcessPureBasicEvents
  
  If Message = #WM_NOTIFY
    If lParam
      ProcessListIconNotification(hWnd, wParam, lParam)
    EndIf
  EndIf
  
  ProcedureReturn iResult
EndProcedure

Procedure Add_Icon_To_ImageList(ImgList, DLL_Path.s, index)
	Protected hIcon, img_index = -1
 	hIcon = ExtractIcon_(#Null, DLL_Path, index)
	If hIcon
		img_index = ImageList_AddIcon_(ImgList, hIcon)
		DestroyIcon_(hIcon)
	Else
		ProcedureReturn -1
	EndIf
	ProcedureReturn img_index
EndProcedure

Procedure ListIcon_SetItemImage(tempListIconGadget, index_item.w, index_subitem.w, index_image.w)
	Protected itemLV.LVITEM ; tagLVITEM
	itemLV\mask = #LVIF_IMAGE
	itemLV\iItem = index_item
	itemLV\iSubItem = index_subitem
	itemLV\iImage = index_image
	If Not SendMessage_(GadgetID(tempListIconGadget), #LVM_SETITEM, 0, @itemLV) 
		Debug "#LVM_SETITEM error"
	EndIf
EndProcedure

;Note: The gadget number must not be zero.
#ListIcon0 = 2
#ListIcon1 = 3
#ListIcon2 = 4

Procedure Event_ResizeWnd()
  Protected Window = EventWindow()
  Protected w = (WindowWidth(Window) - 40) / 3
  Protected h = WindowHeight(Window) - 50
  
  ResizeGadget(#ListIcon0, 10, 40,  w, h)
  ResizeGadget(#ListIcon1, w + 20, 40, w, h)
  ResizeGadget(#ListIcon2, w * 2 + 30, 40, w, h)
  UpdateListIconBkImage(#ListIcon0)
  UpdateListIconBkImage(#ListIcon1)
EndProcedure

Define w, h, i, g, e, Result, Font
Define Window = OpenWindow(#PB_Any, 0, 0, 900, 600, "Image transparency", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)

; To fix some drawing issues.
SmartWindowRefresh(Window, 1)

ButtonGadget(0, 10, 5, 100, 25, "Font 1")
ButtonGadget(1, 120, 5, 100, 25, "Font 2")

w = (WindowWidth(Window) - 40) / 3
h = WindowHeight(Window) - 50

ListIconGadget(#ListIcon0, 10, 40,  w, h, "Col 1", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_MultiSelect)
ListIconGadget(#ListIcon1, w + 20, 40, w, h, "Col 1", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
ListIconGadget(#ListIcon2, w * 2 + 30, 40, w, h, "Plain ListIcon", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_MultiSelect)

For g = #ListIcon0 To #ListIcon2
  AddGadgetColumn(g, 1, "Col 2", 100)
  AddGadgetColumn(g, 2, "Col 3", 100)
  
  Define ImgList = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 10)
  SendMessage_(GadgetID(g), #LVM_SETIMAGELIST, #LVSIL_SMALL, ImgList)
  SendMessage_(GadgetID(g), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_SUBITEMIMAGES, #LVS_EX_SUBITEMIMAGES)
  
  ;For i = 0 To 26
  For i = 0 To 100
    AddGadgetItem(g, i, "item" + i + Chr(10) + "subitem" + i)
  Next
  
  Define icon_index = Add_Icon_To_ImageList(ImgList, "shell32.dll", 296)
  ListIcon_SetItemImage(g, 5, 2, icon_index)
  ListIcon_SetItemImage(g, 6, 2, icon_index)
  
  ; Set the text color.
  SetGadgetItemColor(g, 7, #PB_Gadget_FrontColor, #Red, #PB_All)
  SetGadgetItemColor(g, 8, #PB_Gadget_FrontColor, #Green, #PB_All)
  
  ; No background color of an item can be set.
  SetGadgetItemColor(g, 0, #PB_Gadget_BackColor, #Yellow, #PB_All)
  
  If g <> #ListIcon2
    SetListIconLineColors(g, #White, RGB(Random(255), Random(255), Random(255)))
    
    ; To fix some drawing issues.
    SetProp_(GadgetID(g), #OldWndProc$, SetWindowLongPtr_(GadgetID(g), #GWLP_WNDPROC, @WndProc_ListIcon()))
  EndIf
Next

SetWindowCallback(@WndProc())

BindEvent(#PB_Event_SizeWindow, @Event_ResizeWnd(), Window)

Repeat
  e = WaitWindowEvent()
  
  If e = #PB_Event_Gadget And EventType() = #PB_EventType_LeftClick
    g = EventGadget()
    If GadgetType(g) = #PB_GadgetType_Button
      Result = FontRequester("Arial", 12, 0)
      If Result
        Font = LoadFont(#PB_Any, SelectedFontName(), SelectedFontSize(), SelectedFontStyle())
        If Font
          Select g
            Case 0
              SetGadgetFont(#ListIcon0, FontID(Font))
              SetListIconLineColors(#ListIcon0, -1, -1)
            Case 1
              SetGadgetFont(#ListIcon1, FontID(Font))
              SetListIconLineColors(#ListIcon1, -1, -1)
          EndSelect
        EndIf
      EndIf
    EndIf
  EndIf
  
Until e = #PB_Event_CloseWindow
FreeFont(#PB_All)

Last edited by breeze4me on Sat Apr 06, 2024 9:53 am, edited 4 times in total.
Post Reply