Jump to content

_GUICtrlListView_GetSelectedIndices()


Recommended Posts

  • Moderators
Posted (edited)

pixelsearch,

I believe so. Fortunately the final unnecessary item index just gets rejected by the subsequent _SendMessage/GUICtrlSendMsg command and the function does not fail with an error. Well spotted!

M23

Edit: same error in the release library too - it must have been there for a long time!

Edited by Melba23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites
Posted (edited)

Thanks Melba23 for the confirmation.
Let's notice that the person(s) who scripted  __ArrayDisplay_Share() did it correctly with the corresponding line, used to inspect all LV items during the "Case $idCopy_ID, $idCopy_Data" :

For $i = 0 To GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETITEMCOUNT, 0, 0) - 1

 

Edited by pixelsearch
Link to post
Share on other sites
Posted (edited)

@Melba23 : could you please be kind enough to comment what follows, thanks :)

The function _GUICtrlListView_GetSelectedIndices is based on a loop which checks each and every item of a Listview to retrieve indices of selected item(s), which means it will loop for example 1000 times when there are 1000 items, even if only 3 items are selected. Basically, this is how the function works actually :

$iCount = _GUICtrlListView_GetItemCount($idListView)

For $iItem = 0 To $iCount - 1
    $iRet = GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED)
    If $iRet Then ... ; do here what needs to be done
Next

What about if we script it like that, with a loop executed 3 times only instead of 1000 times, maybe it will end faster while bringing back the same result :

$iSelectedCount = GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0)
If $iSelectedCount = 0 Then Return

$iStart = - 1 ; -1 to find the first item that matches the specified flags (msdn)
              ; the specified item itself is excluded from the search (+++)

For $i = 1 To $iSelectedCount
    $iSelected = GUICtrlSendMsg($idListView, $LVM_GETNEXTITEM, $iStart, $LVNI_SELECTED)
    ; do here what needs to be done
    $iStart = $iSelected
Next

Thanks for your advice.

Edit: I think the message $LVM_GETSELECTEDCOUNT is very fast (tested it on 1000 items)
In case one is reluctant to use it (?) then we could do it in another way (gonna find it back in my yesterday's archive folder and post it below asap)

Ok... here is the other way (I didn't find it in my archive, grr...) so let's hope I "redo" it correctly. This one is without $LVM_GETSELECTEDCOUNT and the loop is still executed 3 times (3 selected items out of 1000 rows). Then the loop may be partially executed a 4th time (exited with ExitLoop) unless the last item is one of the 3 selected items.

$iCount = _GUICtrlListView_GetItemCount($idListView)

$iStart = - 1 ; -1 to find the first item that matches the specified flags (msdn)
              ; the specified item itself is excluded from the search (+++)

For $iItem = 0 To $iCount - 1
    $iSelected = GUICtrlSendMsg($idListView, $LVM_GETNEXTITEM, $iStart, $LVNI_SELECTED)
    If $iSelected = - 1 Then ExitLoop
    ; do here what needs to be done
    $iStart = $iSelected
    $iItem = $iSelected
Next

 

Edited by pixelsearch
Link to post
Share on other sites

Do we even need a  _GUICtrlListView_GetItemCount ?   Because GUICtrlSendMsg($idListView, $LVM_GETNEXTITEM, $iStart, $LVNI_SELECTED) will return an error eventually, no ?

Func _GUICtrlListView_GetSelectedIndicesEX($idLV)
  Local $iIdx = -1 ; -1 to find the first item that matches the specified flags (msdn)
              ; the specified item itself is excluded from the search (+++)
  While True
    $iIdx = GUICtrlSendMsg($idLV, $LVM_GETNEXTITEM, $iIdx, $LVNI_SELECTED)
    If $iIdx = - 1 Then ExitLoop
    ConsoleWrite ($iIdx & @CRLF)
    ; do here what needs to be done
  WEnd
EndFunc

 

Edited by Nine
Link to post
Share on other sites
Posted (edited)

@FrancescoDiMuro : $LVM_GETSELECTEDCOUNT is a message I discovered when I nearly finished my scripting, that's why I had 2 different versions, one using it, the precedent one without it :)

If we are sure that $LVM_GETSELECTEDCOUNT returns a value extremely quickly even on a big LV, then we can use it. If not, it's better to stick to other quick loops (but not to the actual loop as found in  _GUICtrlListView_GetSelectedIndices which sends a message for each and every item)

The only thing I'm sure of is that _GUICtrlListView_GetItemCount() will always return a value in a millisecond , no matter the LV size.

Nine's way is good too, I did it like him a few hours ago (i.e a While loop)

@Nine : concerning the error returned by GUICtrlSendMsg, I'm not sure of anything : see how Melba indicated above that "_SendMessage/GUICtrlSendMsg command and the function does not fail with an error"

His comment concerned the function _GUICtrlListView_GetSelectedIndices with the erroneous line in it ("For $iItem = 0 To $iCount" instead of "For $iItem = 0 To $iCount - 1")

So I tried to understand this kind of "error return" with an example, but I'm not sure I took a good path, here is what I did :

* Amended the function _GUICtrlListView_GetSelectedIndices like this, keeping the erroneous line, adding 3 ConsoleWrite

...
For $iItem = 0 To $iCount
    If IsHWnd($hWnd) Then
        $iRet = _SendMessage($hWnd, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED)
        If @error Then ConsoleWrite("error = " & @error & @CRLF)
        ConsoleWrite("(LVhnd)   $iItem = " & $iItem & "   $iRet = " & $iRet & @CRLF)
    Else
        $iRet = GUICtrlSendMsg($hWnd, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED)
        ConsoleWrite("(LVid )   $iItem = " & $iItem & "   $iRet = " & $iRet & @CRLF)
    EndIf
...

* Amended the script example found in the help file, topic _GUICtrlListView_GetSelectedIndices, adding an optional line to call the function using the LV handle and not it's ID, so we'll try both later :

MsgBox($MB_SYSTEMMODAL, "Information", "Selected Indices: " & _GUICtrlListView_GetSelectedIndices($idListview))

; MsgBox($MB_SYSTEMMODAL, "Information", "Selected Indices: " & _GUICtrlListView_GetSelectedIndices(GUICtrlGetHandle($idListview)))

Also I renamed the 3 Items captions in the example to "Item 0", "Item 1", "Item 2" (better than 1, 2, 3) to match the ConsoleWrite in the function.

* Now when we run the example using the LV id, here is what appears on the screen :

2097891847_LVexample.png.cff814f93631f85efe3b8037ccda90ab.png

Console :

(LVid )   $iItem = 0   $iRet = 0
(LVid )   $iItem = 1   $iRet = 2
(LVid )   $iItem = 2   $iRet = 2
(LVid )   $iItem = 3   $iRet = 0

=> Items 1 and 2 are selected (the code did it) and their $Iret = 2, which corresponds to $LVIS_SELECTED = 0x0002, matching what msdn stipulates for the return value of the LVM_GETITEMSTATE message :

Return value :
Returns the current state for the specified item. The only valid bits in the return value are those that correspond to the bits set in the lParam parameter.

=> Item 0 got $iRet = 0 (not selected by the example code)
=> Item 3... this item doesn't even exist in the LV (that's why we kept the erroneous line "For $iItem = 0 To $iCount") and it also got $iRet = 0, which means that it's not obvious to explain a "failure = 0" when you receive it from GUICtrlSendMsg() : it will be 0 for an existing item or a non-existing item, and unfortunately @error is not an option in GUICtrlSendMsg()

* If we run the example using the LV handle, then the console shows the same :

(LVhnd)   $iItem = 0   $iRet = 0
(LVhnd)   $iItem = 1   $iRet = 2
(LVhnd)   $iItem = 2   $iRet = 2
(LVhnd)   $iItem = 3   $iRet = 0

Same display, but though there is a ConsoleWrite("error = " & @error) added in the function, there is no @error line  appearing in the Console, which means that the _SendMessage() function (found in SendMessage.au3), though it is programmed to return an @error when it happens, doesn't return an error in this case for the non-existing $iItem = 3 (that's what Melba23 meant in his comment)

Func _SendMessage($hWnd, $iMsg, $wParam = 0, $lParam = 0, $iReturn = 0, $wParamType = "wparam", $lParamType = "lparam", $sReturnType = "lresult")
    
    Local $aResult = DllCall("user32.dll", $sReturnType, "SendMessageW", "hwnd", $hWnd, "uint", $iMsg, $wParamType, $wParam, $lParamType, $lParam)
    
    If @error Then Return SetError(@error, @extended, "")
    ...

This is a question I always wanted to ask : what's the use of setting errors in functions (like the _SendMessage() function for example) if @error isn't checked on return, immediately after each _SendMessage() call ?

Edited by pixelsearch
Link to post
Share on other sites

All I was saying is the call to get a count is useless with a loop with the message $LVM_GETNEXTITEM (whatever form you send it)

Link to post
Share on other sites
14 hours ago, pixelsearch said:

This is a question I always wanted to ask : what's the use of setting errors in functions (like the _SendMessage() function for example) if @error isn't checked on return, immediately after each _SendMessage() call ?

in fact the checking if done will identify an internal error from AutoIt code or perhaps Windows code.

Almost all DllCall in the standard UDF have this checking  and return to isolate an error.

you can find that the @error is modify when they  are multiple @error checking in the same function to identify where the error occur just looking at the @error value.

 if _SendMessage parameters arenot  valid it can be

@error:

1 = unable to use the DLL file,  >>> "user32.dll" = Windows pb

>>>  UDF error certainly an Autoit internal error if a valid UDF has been used
2 = unknown "return type",
3 = "function" not found in the DLL file ,
4 = bad number of parameters,
5 = bad parameter.

Link to post
Share on other sites
1 hour ago, jpm said:

in fact the checking if done will identify an internal error from AutoIt code or perhaps Windows code.

Thanks jpm for the explanation. It's always great to have you around... and I bolded the "if" in your answer :D
I understand better now, it's mostly used to identify internal errors.

To test this kind of error, I could reproduce one just now, by altering a line in Func _SendMessage() and replacing a correct parameter with a wrong one :

Original code :

Local $aResult = DllCall("user32.dll", $sReturnType, ...

Wrong code :

Local $aResult = DllCall("user32.dll", "mresult", ...

"mresult" means nothing (it's "lresult" that is expected)

After that, when running the _GUICtrlListView_GetSelectedIndices() example, calling the function with the LV handle, I'm getting a wrong result in the final display (no selected indices) because of @error = 2  (2 = unknown "return type") and if I check the error inside the UDF, then I can (finally !) display an "error 2" in the Console :

$aResult = 0   VarGetType($aResult) = Int32
$aResult = 0   VarGetType($aResult) = Int32
$aResult = 0   VarGetType($aResult) = Int32
error = 2   extended = 0   $iRet = 0

If I don't add any error checking in the function _GUICtrlListView_GetSelectedIndices(), then I'll "simply" get a wrong result in the script, not being notified that @error was once equal to 2

Ok, time to replace both altered UDF's with the right ones :)

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...