Modify

Opened 6 years ago

Closed 6 years ago

#2927 closed Bug (Works For Me)

Adding Items with GUICtrlCreateListViewItem will cause GUI freeze if a subitem is empty

Reported by: Trolleule Owned by:
Milestone: Component: AutoIt
Version: 3.3.12.0 Severity: None
Keywords: GUICtrlCreateListViewItem, WM_NOTIFY, WM_DRAWITEM Cc:

Description

When i add listview items with GUICtrlCreateListViewItem to an ownerdraw listview control and there is a empty subitem in the subitem chain and WM_NOTIFY is present, the LV control freezes. Adding items with UDF works.

I noticed that even when the display of the LV is frozen, the WM_NOTIFY function is still active and responding. I put a consolewrite in the WM_NOTIFY function and could see writing after the listview display froze. Surprisingly, minimizing the GUI unfreezes the listview and all controls on the GUI.

#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ColorConstants.au3>


Global Const $ODT_LISTVIEW = 102
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4
Global Const $ODS_SELECTED = 0x0001


Global $GUI_main = GUICreate("", 300, 300, -1, -1)
Global $hGUI_tab_listview[2][10]


$hGUI_tab_listview[0][0] = GUICtrlCreateListView("A|B|C", 15, 10, 250, 280, _
  BitOR($LVS_REPORT, $LVS_SHOWSELALWAYS, $LVS_OWNERDRAWFIXED, $WS_BORDER, $WS_CLIPCHILDREN), 0) ; + $LVS_EX_CHECKBOXES + $LVS_SINGLESEL
_GUICtrlListView_SetExtendedListViewStyle($hGUI_tab_listview[0][0], $LVS_EX_DOUBLEBUFFER)


For $i = 0 To 50
;~  _GUICtrlListView_AddItem(-1, "test " & $i)
;~  _GUICtrlListView_AddSubItem(-1, $i, $i, 1)
    If $i = 5 Then
        GUICtrlCreateListViewItem("|" & "sdfdsfs" & "|" & "", $hGUI_tab_listview[0][0]) ; if empty slot is present, gui gets freezed ; comment this lines out for check
        ContinueLoop
    EndIf
    GUICtrlCreateListViewItem("|" & "sdfdsfs" & "|" & $i, $hGUI_tab_listview[0][0])
Next

GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState(@SW_SHOW)


; Loop until the user exits.
While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE, $idOK
            ExitLoop
    EndSwitch
WEnd


Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Return $GUI_RUNDEFMSG
EndFunc     ;==>WM_NOTIFY


Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam)
    Local $tagDRAWITEMSTRUCT, $iBrushColor, $cID, $itmID, $itmAction, $itmState, $hItm, $hDC, $bSelected
    $tagDRAWITEMSTRUCT = DllStructCreate( _
       "uint cType;" & _
       "uint cID;" & _
       "uint itmID;" & _
       "uint itmAction;" & _
       "uint itmState;" & _
       "hwnd hItm;" & _
       "handle hDC;" & _
       "long itmRect[4];" & _
       "ulong_ptr itmData" _
       , $lParam)
    If DllStructGetData($tagDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG
    $cID = DllStructGetData($tagDRAWITEMSTRUCT, "cID")
    $itmID = DllStructGetData($tagDRAWITEMSTRUCT, "itmID")
    $itmAction = DllStructGetData($tagDRAWITEMSTRUCT, "itmAction")
    $itmState = DllStructGetData($tagDRAWITEMSTRUCT, "itmState")
    $hItm = DllStructGetData($tagDRAWITEMSTRUCT, "hItm")
    $hDC = DllStructGetData($tagDRAWITEMSTRUCT, "hDC")
    $bSelected = BitAND($itmState, $ODS_SELECTED)
    Switch $cID ; will look for ControlID, not window handle.
      Case $hGUI_tab_listview[0][0]
       Switch $itmAction
        Case $ODA_DRAWENTIRE
         ; don't forget, this is BGR, not RGB
         If $itmState = $bSelected Then ; item is not selected
          $iBrushColor = 0xEEDDBB
         Else ; item is selected
          $iBrushColor = $COLOR_RED
         EndIf
         Local $aBrush = _WinAPI_CreateSolidBrush($iBrushColor)
         Local $aBrushOld = _WinAPI_SelectObject($hDC, $aBrush)
         Local $iLeft = DllStructGetData($tagDRAWITEMSTRUCT, "itmRect", 1)
         DllStructSetData($tagDRAWITEMSTRUCT, "itmRect", $iLeft + 1, 1) ; rectangle coordinates for coloring
         ; +1 is the left margin
         _WinAPI_FillRect($hDC, DllStructGetPtr($tagDRAWITEMSTRUCT, "itmRect"), $aBrush)
         _WinAPI_SelectObject($hDC, $aBrushOld)
         _WinAPI_DeleteObject($aBrush)

         ; for all columns of the row:
         $local_alignment = $DT_LEFT
         For $i = 0 To _GUICtrlListView_GetColumnCount($hGUI_tab_listview[0][0]) - 1
          ; 1. get subitem text:
          Local $iSubItmText = _GUICtrlListView_GetItemText($hGUI_tab_listview[0][0], $itmID, $i)
          ; If $iSubItmText = "" Then ContinueLoop ;ConsoleWrite("hier")
          ; 2. get subitem coordinates for drawing its respective text
          Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hGUI_tab_listview[0][0], $itmID, $i)
          ; the function above accepts not only subitems (one-based index), but also main item (index=0)
          ; 3. pass the coordinates to a DLL struct
          Local $iSubItmRect = DllStructCreate("long[4]")
          DllStructSetData($iSubItmRect, 1, $aSubItmRect[0] + 6, 1) ; +6 is left margin (X)
          DllStructSetData($iSubItmRect, 1, $aSubItmRect[1] + (-2), 2) ; + (-2) is upper margin (Y)
          DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3)
          DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4)
          _WinAPI_SetTextColor($hDC, $COLOR_RED)

          DllCall("user32.dll", "int", "DrawTextW", "hwnd", $hDC, "wstr", $iSubItmText, "int", StringLen($iSubItmText), _
            "ptr", DllStructGetPtr($iSubItmRect), "int", $local_alignment)
         Next
         ;#ce
       EndSwitch
    EndSwitch
Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

Attachments (0)

Change History (7)

comment:1 Changed 6 years ago by Jpm

I don't see any freeze Under my Windows8.1

Mixing UDF and standard AutoIt GUI is not a great idea specialy trying to register message.
some time no conflict sometime conflict as 2 separate handling without any communication between them

comment:3 Changed 6 years ago by Melba23

Trolleule,

And as discussed in that thread I cannot make the GUI freeze other then by resizing the columns - which I fixed by amending the redraw process. Certainly scrolling the ListView does not induce a freeze in either 3.3.12.0 or 3.3.13.19, Win7 x32.

M23

comment:4 Changed 6 years ago by Trolleule

There is definitive a bug. Look what BrewManNH has written. When i scroll with mouse fast up and down more than one time, anytime the listview/ gui freeze and the LV becomes empty. Minimizing and Maximizing restores the LV. If you uncomment WM_NOTIFY registering, it works fine. Using Win7 x86.

Use this example, this will avoid missunderstandings:

#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ColorConstants.au3>


Global Const $ODT_LISTVIEW = 102
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4
Global Const $ODS_SELECTED = 0x0001


Global $GUI_main = GUICreate("", 300, 300, -1, -1)


Global $idLV = GUICtrlCreateListView("A|B|C", 15, 10, 250, 280, _
  BitOR($LVS_REPORT, $LVS_SHOWSELALWAYS, $LVS_OWNERDRAWFIXED, $WS_BORDER, $WS_CLIPCHILDREN), -1) ; + $LVS_EX_CHECKBOXES + $LVS_SINGLESEL
;~ _GUICtrlListView_SetExtendedListViewStyle($idLV, $LVS_EX_DOUBLEBUFFER)


For $i = 0 To 50
;~  _GUICtrlListView_AddItem(-1, "test " & $i)
;~  _GUICtrlListView_AddSubItem(-1, $i, $i, 1)
    If $i = 5 Then
        GUICtrlCreateListViewItem("|" & "" & "|" & "", $idLV) ; if empty slot is present, gui gets freezed ; comment this lines out for check
        ContinueLoop
    EndIf
    GUICtrlCreateListViewItem("|" & "sdfdsfs" & "|" & $i, $idLV)
Next

GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState(@SW_SHOW)


; Loop until the user exits.
While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE, $idOK
            ExitLoop
    EndSwitch
WEnd


Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Return $GUI_RUNDEFMSG
EndFunc     ;==>WM_NOTIFY


Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam)
    Local $tagDRAWITEMSTRUCT, $iBrushColor, $cID, $itmID, $itmAction, $itmState, $hItm, $hDC, $bSelected

    $tagDRAWITEMSTRUCT = DllStructCreate("uint cType;uint cID;uint itmID;uint itmAction;uint itmState;" & _
                                         "hwnd hItm;hwnd hDC;int itmRect[4];dword itmData", $lParam)

    If DllStructGetData($tagDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG

    $cID = DllStructGetData($tagDRAWITEMSTRUCT, "cID")
    $itmID = DllStructGetData($tagDRAWITEMSTRUCT, "itmID")
    $itmAction = DllStructGetData($tagDRAWITEMSTRUCT, "itmAction")
    $itmState = DllStructGetData($tagDRAWITEMSTRUCT, "itmState")
    $hItm = DllStructGetData($tagDRAWITEMSTRUCT, "hItm")
    $hDC = DllStructGetData($tagDRAWITEMSTRUCT, "hDC")

    $bSelected = BitAND($itmState, $ODS_SELECTED)

    Switch $itmAction
        Case $ODA_DRAWENTIRE
            If $itmState = $bSelected Then
                $iBrushColor = 0xFFFFFF
            Else
                $iBrushColor = 0x0000FF
            EndIf

            Local $aBrush = DLLCall("gdi32.dll","hwnd","CreateSolidBrush", "int", $iBrushColor)

            Local $aBrushOld = _WinAPI_SelectObject($hDC, $aBrush[0])

            Local $iLeft = DllStructGetData($tagDRAWITEMSTRUCT, "itmRect", 1)
            DllStructSetData($tagDRAWITEMSTRUCT, "itmRect", $iLeft + 5, 1)

            _WinAPI_FillRect($hDC, DllStructGetPtr($tagDRAWITEMSTRUCT, "itmRect"), $aBrush[0])

            _WinAPI_SelectObject($hDC, $aBrushOld)

            _WinAPI_DeleteObject($aBrush[0])

            Local $itmText = _GUICtrlListView_GetItemText($hItm, $itmID)

            DllStructSetData($tagDRAWITEMSTRUCT, "itmRect", DllStructGetData($tagDRAWITEMSTRUCT, "itmRect", 1) + 2, 1)

            DllCall("user32.dll", "int", "DrawText", "hwnd", $hDC, "str", $itmText, "int", StringLen($itmText), _
                    "ptr", DllStructGetPtr($tagDRAWITEMSTRUCT, "itmRect"), "int", $DT_LEFT)

            Local $iSubItmText = _GUICtrlListView_GetItemText($hItm, $itmID, 1)
            Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hItm, $itmID, 1)

            Local $iSubItmRect = DllStructCreate("int[4]")

            DllStructSetData($iSubItmRect, 1, $aSubItmRect[0], 1)
            DllStructSetData($iSubItmRect, 1, $aSubItmRect[1], 2)
            DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3)
            DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4)

            DllCall("user32.dll", "int", "DrawText", "hwnd", $hDC, "str", $iSubItmText, "int", StringLen($iSubItmText), _
                    "ptr", DllStructGetPtr($iSubItmRect), "int", $DT_LEFT)
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc

comment:5 Changed 6 years ago by Jpm

I still cannot reproduce under Win8.1 update 1

comment:6 Changed 6 years ago by Melba23

Trolleule,

I made that code crash just once in multiple attempts and when it did it was obvious that the problem lies in the redrawing as the ListView continued to scroll but was gradually emptied of its contents. So I think that rather than having found an AutoIt bug you have a serious problem in your WM_DRAWITEM handler and you should return to the forum and seek advice.

M23

comment:7 Changed 6 years ago by Jpm

  • Resolution set to Works For Me
  • Status changed from new to closed

I agree with M23 remark your WM_DRAWITEM has a pb so go to the forum to get help to locate it
I will close it
Just reopen a new one if you really islotae the pb.
Cheers

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.