Sign in to follow this  
Followers 0
Zedna

GUICtrlSetImage() destroy imagelist in ListView header

7 posts in this topic

#1 ·  Posted (edited)

Maybe it's bug in GUICtrlSetImage() but I'm not sure so I post it here.

Please can somebody look at it?

I have workaround for that (restore header imagelist)

but I prefer some explanation or correction.

I simplified my huge LOG_View project to this smaller testing script

but it's still rather big so sorry for that. I didn't want to remove core functions related to problem.

Some notes:

- App consinst from ListView in report style with some ListView items 

- with ListView header is asociated dynamic created imagelist to show green arrow in header column representing sorting

- with ListView items is used GUICtrlSetImage() to set item icon from resources (icons.res)

Problem is when I use GUICtrlSetImage() to set items icons then green arrow in ListView header is destroyed and is shown icons from first and second ListView item (instead of green arrow from my imagelist)

For simpler demonstation I created ListView without GUICtrlSetImage() first to show green arrow is shown correctly after clicking on column header.

When you click button ""GUICtrlSetImage" then is applied  GUICtrlSetImage() to ListView items and from now is not shown green arrow in column header after click on header column (instead is shown icon from first and second ListView item).

When you check "Workaround checkbox" then is applied my workaround so after click on column header is shown green arrow correctly.

My workaround uses SendMessage with HDM_SETIMAGELIST which restores imagelist.

Here are screenshots and sources and all stuff (all_stuff.zip: ICONS.RC,ICONS.RES,log_view.au3)

#AutoIt3Wrapper_useupx=n
#AutoIt3Wrapper_run_after=ResHacker.exe -add %out%, %out%, icons.res ,,,
#AutoIt3Wrapper_run_after=upx.exe --compress-icons=0 "%out%"

#NoTrayIcon 
#include <GUIConstants.au3>
#include <GuiListView.au3>

Global Const $LVM_GETITEM = $LVM_FIRST + 5

Global Const $ILC_MASK = 0x0001
Global Const $ILC_COLOR32 = 0x0020
Global Const $HDM_SETIMAGELIST = 0x1208
Global Const $HDM_SETITEM = 0x1204
Global Const $HDI_FORMAT = 0x4
Global Const $HDI_IMAGE = 0x20

Global Const $HDF_STRING = 0x4000
Global Const $HDF_IMAGE = 0x800
Global Const $HDF_BITMAP_ON_RIGHT = 0x1000

Global $nCurCol = -1, $nSortDir = 1, $bSet = 0, $nCol = -1, $Done = 0
Global $Header, $HD_ITEM, $LViewImageList

$Form1 = GUICreate("ListView header imagelist test", 300, 250)
$ListView1 = GUICtrlCreateListView("Col1|Col2|Col3", 10, 10, 280, 180, BitOR($LVS_REPORT,$LVS_SINGLESEL,$LVS_SHOWSELALWAYS,$WS_HSCROLL,$WS_VSCROLL,$WS_BORDER), BitOR($WS_EX_CLIENTEDGE,$LVS_EX_FULLROWSELECT))
$btn1 = GUICtrlCreateButton("GUICtrlSetImage", 10, 200)
$check1 = GUICtrlCreateCheckbox("Workaround", 150, 200)
GUISetState(@SW_SHOW)

; init header imagelist
$HD_ITEM = DllStructCreate("int;int;ptr;int;int;int;int;int;int")
$LViewImageList = ImageList_Create(16, 16, BitOR($ILC_MASK, $ILC_COLOR32), 2, 0)
$stIcon = DllStructCreate("int")
ExtractIconEx(@ScriptFullPath, 3, 0, DllStructGetPtr($stIcon), 1) ; sort_asc.ico
ImageList_AddIcon($LViewImageList, DllStructGetData($stIcon, 1))
ExtractIconEx(@ScriptFullPath, 4, 0, DllStructGetPtr($stIcon), 1) ; sort_desc.ico
ImageList_AddIcon($LViewImageList, DllStructGetData($stIcon, 1))
DestroyIcon(DllStructGetData($stIcon, 1))
$stIcon = 0
$Header = GUICtrlSendMsg($ListView1, $LVM_GETHEADER, 0, 0)
DllCall("user32.dll", "int", "SendMessage", "hwnd", $Header, "int", $HDM_SETIMAGELIST, "int", 0, "hwnd", $LViewImageList)

; add some data
$item1 = GUICtrlCreateListViewItem('Data11|Data12|Data13', $ListView1)
$item2 = GUICtrlCreateListViewItem('Data21|Data22|Data23', $ListView1)
$item3 = GUICtrlCreateListViewItem('Data31|Data32|Data33', $ListView1)

GUICtrlSendMsg($ListView1, $LVM_SETCOLUMNWIDTH, 0, 75)
GUICtrlSendMsg($ListView1, $LVM_SETCOLUMNWIDTH, 1, 75)
GUICtrlSendMsg($ListView1, $LVM_SETCOLUMNWIDTH, 2, 75)

GUICtrlRegisterListViewSort($ListView1, "LVSort")

While 1
    $msg = GuiGetMsg()
    Switch $msg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $btn1
            ; this destroy listview header imagelist
            GUICtrlSetImage($item1, @ScriptFullPath, -6) 
            GUICtrlSetImage($item2, @ScriptFullPath, -8)
            GUICtrlSetImage($item3, @ScriptFullPath, -10)
        Case $ListView1
            $Done = 0
            $bSet = 0
            $nCurCol = $nCol
    EndSwitch
WEnd

; sorting function, also sets sort icon in column header
Func LVSort($hWnd, $nItem1, $nItem2, $nColumn)
    ; Switch the sorting direction
    If $nColumn = $nCurCol Then
        If Not $bSet Then
            $nSortDir = $nSortDir * -1
            $bSet = 1
        EndIf
    Else
        $nSortDir = 1
    EndIf
        If $Done = 0 AND $nSortDir = 1 Then
        SetHeaderIcon($nColumn, 0)
        $Done = 1
    ElseIf $Done = 0 AND $nSortDir = -1 Then
        SetHeaderIcon($nColumn, 1)
        $Done = 1
    Endif

    $nCol = $nColumn

    $val1 = GetSubItemText($ListView1, $nItem1, $nColumn)
    $val2 = GetSubItemText($ListView1, $nItem2, $nColumn)

    $nResult = 0       ; No change of item1 and item2 positions

    If $val1 < $val2 Then
        $nResult = -1  ; Put item2 before item1
    ElseIf  $val1 > $val2 Then
        $nResult = 1   ; Put item2 behind item1
    EndIf
        Return $nResult * $nSortDir
EndFunc

Func SetHeaderIcon($iCol, $iSortOrder)
    ; after GUICtrlSetImage header imagelist gets corrupted 
    ; workaround: restore header imagelist
    If BitAnd(GUICtrlRead($check1),$GUI_CHECKED) = $GUI_CHECKED Then
        DllCall("user32.dll", "int", "SendMessage", "hwnd", $Header, "int", $HDM_SETIMAGELIST, "int", 0, "hwnd", $LViewImageList)
    EndIf
    DllStructSetData($HD_ITEM,1,BitOR($HDI_IMAGE,$HDI_FORMAT))
    DllStructSetData($HD_ITEM,8,$iSortOrder)
    For $x = 0 To 2
        If $x = $iCol Then
            DllStructSetData($HD_ITEM,6,BitOR($HDF_STRING,$HDF_IMAGE,$HDF_BITMAP_ON_RIGHT))
        Else
            DllStructSetData($HD_ITEM,6,$HDF_STRING)
        EndIf
        DllCall("user32.dll", "int", "SendMessage", "hwnd", $Header, "int", $HDM_SETITEM, "int", $x, "ptr", DllStructGetPtr($HD_ITEM))
    Next
EndFunc

; ############### include files ################

Func GetSubItemText($nCtrlID, $nItemID, $nColumn)
    Local $stLvfi       = DllStructCreate("uint;ptr;int;int[2];int")
    DllStructSetData($stLvfi, 1, $LVFI_PARAM)
    DllStructSetData($stLvfi, 3, $nItemID)

    Local $stBuffer     = DllStructCreate("char[260]")
    $nIndex = GUICtrlSendMsg($nCtrlID, $LVM_FINDITEM, -1, DllStructGetPtr($stLvfi))

    Local $stLvi        = DllStructCreate("uint;int;int;uint;uint;ptr;int;int;int;int")
    DllStructSetData($stLvi, 1, $LVIF_TEXT)
    DllStructSetData($stLvi, 2, $nIndex)
    DllStructSetData($stLvi, 3, $nColumn)
    DllStructSetData($stLvi, 6, DllStructGetPtr($stBuffer))
    DllStructSetData($stLvi, 7, 260)

    GUICtrlSendMsg($nCtrlID, $LVM_GETITEM, 0, DllStructGetPtr($stLvi));

    $sItemText  = DllStructGetData($stBuffer, 1)
    $stLvi = 0
    $stLvfi = 0
    $stBuffer = 0

    Return $sItemText
EndFunc

Func ImageList_Create($nImageWidth, $nImageHeight, $nFlags, $nInitial, $nGrow)
   $hImageList = DllCall('comctl32.dll', 'hwnd', 'ImageList_Create', 'int', $nImageWidth, 'int', $nImageHeight, 'int', $nFlags, 'int', $nInitial, 'int', $nGrow)
   Return $hImageList[0]
EndFunc

Func ImageList_AddIcon($hIml, $hIcon)
   $nIndex = DllCall('comctl32.dll', 'int', 'ImageList_AddIcon', 'hwnd', $hIml, 'hwnd', $hIcon)
   Return $nIndex[0]
EndFunc

Func ImageList_Destroy($hIml)
   $bResult = DllCall('comctl32.dll', 'int', 'ImageList_Destroy', 'hwnd', $hIml)
   Return $bResult[0]
EndFunc

Func ExtractIconEx($sIconFile, $nIconID, $ptrIconLarge, $ptrIconSmall, $nIcons)
   $nCount = DllCall('shell32.dll', 'int', 'ExtractIconEx', 'str', $sIconFile, 'int', $nIconID, 'ptr', $ptrIconLarge, 'ptr', $ptrIconSmall, 'int', $nIcons)
   Return $nCount[0]
EndFunc

Func DestroyIcon($hIcon)
   $bResult = DllCall('user32.dll', 'int', 'DestroyIcon', 'hwnd', $hIcon)
   Return $bResult[0]
EndFunc

post-6483-1188488172_thumb.png

post-6483-1188488182_thumb.png

all_stuff.zip

Edited by Zedna

Share this post


Link to post
Share on other sites

no one?

I don't get any icons displayed when I try your script Zedna. (I compiled it.)

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

I don't get any icons displayed when I try your script Zedna. (I compiled it.)

Sorry

reshacker.exe and upx.exe must be in script directory (or in directory mentioned in global PATH - this is my case) at compile time

because I used relative path in #AutoIt3Wrapper directive (for simplicity).

Edited by Zedna

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

It certainly seems that the first call to GuiCtrlSetImage destroys the ListView-Header ImageList.

So one solution is not to use GuiCtrlSetImage calls but only ImageList_xxx api calls.

Perhaps this is complicated. However there are two more proposals [A] and that I could think of (that do not need the workaround of first post)

[A]

1. Initialize autoit ImageList by a using GuiCtrlSetImage with an empty icon

2. Initialize header with ImageList_Create, ImageList_AddIcon, ..., $HDM_SETIMAGELIST msg

; init header imagelist
$HD_ITEM = DllStructCreate("int;int;ptr;int;int;int;int;int;int")
$LViewImageList = ImageList_Create(16, 16, BitOR($ILC_MASK, $ILC_COLOR32), 2, 0)

GUICtrlSetImage($ListView1, @ScriptFullPath, -15) ; empty icon

$stIcon = DllStructCreate("int")
ExtractIconEx(@ScriptFullPath, 3, 0, DllStructGetPtr($stIcon), 1) ; sort_asc.ico
ImageList_AddIcon($LViewImageList, DllStructGetData($stIcon, 1))

ExtractIconEx(@ScriptFullPath, 4, 0, DllStructGetPtr($stIcon), 1) ; sort_desc.ico
ImageList_AddIcon($LViewImageList, DllStructGetData($stIcon, 1))
DestroyIcon(DllStructGetData($stIcon, 1))
$stIcon = 0

$Header = GUICtrlSendMsg($ListView1, $LVM_GETHEADER, 0, 0)
DllCall("user32.dll", "int", "SendMessage", "hwnd", $Header, "int", $HDM_SETIMAGELIST, "int", 0, "hwnd", $LViewImageList)
Edited by aec

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0