Opened 3 years ago

Last modified 3 years ago

#3848 closed Bug

_GUICtrlListView_GetItemTextArray does not return an error for multi-column listview if no row is selected. — at Version 2

Reported by: BugFix Owned by:
Milestone: 3.3.15.5 Component: Standard UDFs
Version: 3.3.14.5 Severity: None
Keywords: Cc:

Description (last modified by mLipok)

I had written a function _GUICtrlListView_SetItemTextArray as a counterpart to _GUICtrlListView_GetItemTextArray.
In doing so, I noticed that it was not possible to determine if _GUICtrlListView_GetItemTextArray was successful. In the help it says:
Failure: Array with the following format: [0] - Number of Columns in array (0).
This is only correct for the case of a listview with one column. Otherwise an empty array with column count +1 elements is returned: [iCount, '', '', ..]. But this cannot be distinguished from a valid empty array (row could be empty).
This is caused by the function _GUICtrlListView_GetItemTextString. In this function, if -1 is passed as index, the index of the selected row is used. This is returned by _GUICtrlListView_GetNextItem. However, if no line is marked, the return is -1.
Since this value is not checked, the queries of all (sub)items for index -1 are now executed with _GUICtrlListView_GetItemText. This causes empty strings and data separators to be strung together.
The result, although false, is returned without error detection.

In my modifications (_GUICtrlListView_GetItemTextArray_MOD, _GUICtrlListView_GetItemTextString_MOD) I have integrated the index check and thus it can be determined whether _GUICtrlListView_GetItemTextArray() was successful. (see my test script)
In general, I think it is recommended that all functions that work with indexes also check them for validity. Many functions call each other and the user has no possibility to check their result and find sources of errors.

#include <GuiListView.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <Array.au3>

$gui = GUICreate('Test')
$lv = GuiCtrlCreateListView('a|b|c', 10, 10, 350, 250)
For $i = 1 To 30
        GUICtrlCreateListViewItem(StringFormat('%d|%d|%d', $i-1, Random(1,10,1), Random(1,10,1)), $lv)
Next
$hLV = GUICtrlGetHandle($lv)
_GUICtrlListView_SetExtendedListViewStyle($hLV, BitOR($LVS_EX_FULLROWSELECT,$WS_EX_CLIENTEDGE,$LVS_EX_GRIDLINES))
_GUICtrlListView_SetColumnWidth($hLV, 0, 85)
_GUICtrlListView_SetColumnWidth($hLV, 1, 85)
_GUICtrlListView_SetColumnWidth($hLV, 2, $LVSCW_AUTOSIZE_USEHEADER)
$bt1 = GUICtrlCreateButton('Set selected to 3', 10, 280, 120, 24)
$bt2 = GUICtrlCreateButton('Set 5 to selected', 140, 280, 120, 24)
$btRemove = GUICtrlCreateButton('Remove Selection', 270, 280, 120, 24)
$cb = GUICtrlCreateCheckbox(' Use functions with Index check!', 10, 320)
$bCheck = False

GUISetState()

$f = _GUICtrlListView_GetItemTextArray
$fCheck = _GUICtrlListView_GetItemTextArray_MOD

While True
        $bCheck = BitAND(GUICtrlRead($cb), $GUI_CHECKED) ? True : False
        Switch GUIGetMsg()
                Case -3
                        Exit
                Case $bt1
                        If _GUICtrlListView_SetItemTextArray($hLV, ($bCheck ? $fCheck($hLV) : $f($hLV)), 3) <> 1 Then _
                                MsgBox(0, 'Error', 'None Selection')
                Case $bt2
                        If _GUICtrlListView_SetItemTextArray($hLV, ($bCheck ? $fCheck($hLV, 5) : $f($hLV, 5))) <> 1 Then _
                                MsgBox(0, 'Error', 'None Selection')
                Case $btRemove
                        _GUICtrlListView_SetItemSelected($hLV, _GUICtrlListView_GetNextItem($hLV), False, False)
        EndSwitch
WEnd



; #FUNCTION# ====================================================================================================================
; Author ........: Gary Frost (gafrost)
; Modified.......: @BugFix
; ===============================================================================================================================
Func _GUICtrlListView_GetItemTextArray_MOD($hWnd, $iItem = -1)

;~      Local $sItems = _GUICtrlListView_GetItemTextString($hWnd, $iItem)
        ; Here occurs the error. This function produces a wrong output without @error.
        ; i.e.: listview with 3 columns, none selection but "$iItem = -1" - should use the current selection
        ; $sItems = "||", none error!

        Local $sItems = _GUICtrlListView_GetItemTextString_MOD($hWnd, $iItem)

        If $sItems = "" Then
                Local $aItems[1] = [0]
                Return SetError($LV_ERR, $LV_ERR, $aItems)
        EndIf
        Return StringSplit($sItems, Opt('GUIDataSeparatorChar'))
EndFunc   ;==>_GUICtrlListView_GetItemTextArray

; #FUNCTION# ====================================================================================================================
; Author ........: Gary Frost (gafrost)
; Modified.......: @BugFix
; ===============================================================================================================================
Func _GUICtrlListView_GetItemTextString_MOD($hWnd, $iItem = -1)
        Local $sRow = "", $sSeparatorChar = Opt('GUIDataSeparatorChar'), $iSelected
        If $iItem = -1 Then
                ; '$iItem = -1' says: use the current selection
                $iSelected = _GUICtrlListView_GetNextItem($hWnd) ; get current row selected
                ; But this function returns also "-1" if none item is selected
        Else
                $iSelected = $iItem ; get row
        EndIf

        ; here is required to check if _GUICtrlListView_GetNextItem() has failed
        If $iSelected < 0 Or $iSelected > _GUICtrlListView_GetItemCount($hWnd) - 1 Then Return SetError(1,0,0)

        For $x = 0 To _GUICtrlListView_GetColumnCount($hWnd) - 1
                ; _GUICtrlListView_GetItemText() it does not matter at all whether a valid index was passed.
                ; In case of error there is no message and an empty string is returned. This cannot be distinguished from an actually empty item.
                ; For this reason, a wrong index must be caught before calling the function.
                $sRow &= _GUICtrlListView_GetItemText($hWnd, $iSelected, $x) & $sSeparatorChar
        Next
        Return StringTrimRight($sRow, 1)
EndFunc   ;==>_GUICtrlListView_GetItemTextString

Change History (2)

comment:1 Changed 3 years ago by BugFix

Sorry, forgot to attach my function _GUICtrlListView_SetItemTextArray, here it is.

; #FUNCTION# ====================================================================================================================
; Name ..........: _GUICtrlListView_SetItemTextArray
; Description ...: Sets the text for a listview item with all subitem.
; Parameters ....: $_hWnd   The listview ID or handle
; ...............: $_aText  Array with item text, like result from _GUICtrlListView_GetItemTextArray()
; ...............: $_iItem  The item index, which text want be set (Default = -1, the current selected item)
; Return values .: Success      1
; ...............: Failure      0       @error: 1 - none or empty text array,   2 - wrong column count in text array
; ...............:                                              3 - none selected item,                 4 - wrong item index passed
; Author ........: BugFix
; Remarks .......: This function is the counterpart of _GUICtrlListView_GetItemTextArray.
; ===============================================================================================================================
Func _GUICtrlListView_SetItemTextArray($_hWnd, $_aText, $_iItem=-1)
        If Not IsHWnd($_hWnd) Then $_hWnd = GUICtrlGetHandle($_hWnd)
        If Not IsArray($_aText) Or ($_aText[0] = 0) Then Return SetError(1,0,0)  ; $_aText[0] was never 0, if _GUICtrlListView_GetItemTextArray() was used without selection
        Local $iCountCol = _GUICtrlListView_GetColumnCount($_hWnd)
        If $_aText[0] <> $iCountCol Then Return SetError(2,0,0)
        If $_iItem = -1 Then
                Local $iSel = _GUICtrlListView_GetNextItem($_hWnd) ; with defaults - the first selected item
                If $iSel = -1 Then Return SetError(3,0,0) ; none selection
                $_iItem = $iSel
        Else
                If $_iItem > _GUICtrlListView_GetItemCount($_hWnd) -1 Then Return SetError(4,0,0)
        EndIf
        For $i = 1 To $iCountCol
                _GUICtrlListView_SetItemText($_hWnd, $_iItem, $_aText[$i], $i -1)
        Next
        Return 1
EndFunc
Last edited 3 years ago by mLipok (previous) (diff)

comment:2 Changed 3 years ago by mLipok

  • Description modified (diff)
Note: See TracTickets for help on using tickets.