timbo Posted May 31, 2011 Posted May 31, 2011 (edited) Hello All, Unfortunately I imagine I am asking the impossible, but I will make my attempt anyway. Overview: Basically I would like to sort the same column twice without affecting the Asc/Desc (i.e. If currently sorted in Asc order, then sort again in Asc order). e.g. if an item value has dynamically changed, and it needs to be sorted back into the correct sequence. Scenario: I have a listview which has items dynamically updated (via GUICtrlSetData Or _GUICtrlListView_SetItem, not important). When I have the listview sorted, and one of those items is updated, I need to trigger a sort again (to reposition the item in the correct order). This can easily be done by calling "_GUICtrlListView_SimpleSort" and toggling the "$B_DESCENDING" value (will result in the column being sorted using same Asc/Desc order as previously called). Problem: As many of you know "SimpleSort" sorts the values (via an array), then repopulates the listview in such a way that the values no longer relate to the original "controlID" that it was created with (via "GUICtrlCreateListViewItem"). On the other hand, "_GUICtrlListView_SortItems" manages to sort the values while maintaining the "controlID". This means that formatting (set via "GUICtrlSetBkColor" and "GUICtrlSetColor") stick to the intended values. Example: I only just figured all this out myself, so I thought I should provide some code to help others understand: expandcollapse popup#include <GUIConstantsEx.au3> #include <GuiListView.au3> #include <WindowsConstants.au3> Opt('MustDeclareVars', 1) Example() Func Example() GUICreate("listview items", 220, 250, 100, 200, -1) Local $listview = GUICtrlCreateListView("Col 0|Col 1|Col 2", 10, 10, 200, 170) Local $B_DESCENDING[3] Local $listValues[10] For $i = 0 To UBound($listValues)-1 If $i = 2 Then $listValues[$i] = GUICtrlCreateListViewItem("Green|"&($i+1)&"|"&($i+1), $listview) Global $greenVal=($i+1) ElseIf $i = 6 Then $listValues[$i] = GUICtrlCreateListViewItem("Red|"&($i+1)&"|"&($i+1), $listview) ElseIf $i = 8 Then $listValues[$i] = GUICtrlCreateListViewItem("Blue|"&($i+1)&"|"&($i+1), $listview) Else $listValues[$i] = GUICtrlCreateListViewItem("White|"&($i+1)&"|"&($i+1), $listview) EndIf Next GUICtrlSetBkColor($listValues[2],0xD0FFD0) GUICtrlSetBkColor($listValues[6],0xFFD0D0) GUICtrlSetBkColor($listValues[8],0xCDDEFD) Local $button = GUICtrlCreateButton("Update green row and re-sort", 10, 185, 200, 20) Local $radio[2] $radio[0] = GUICtrlCreateRadio("Sort via SortItems", 5, 210, 200, 20) $radio[1] = GUICtrlCreateRadio("Sort via SimpleSort", 5, 230, 200, 20) GUICtrlSetState($radio[0], $GUI_CHECKED) GUISetState() _GUICtrlListView_RegisterSortCallBack($listview) Global $lastSortCol=2 _GUICtrlListView_SortItems($listview, $lastSortCol) ;Sort by Col 2 on open Do Local $msg = GUIGetMsg() Select Case $msg = $listview If BitAND(GUICtrlRead($radio[0]), $GUI_CHECKED) = $GUI_CHECKED Then $lastSortCol=GUICtrlGetState($listview) _GUICtrlListView_SortItems($listview, $lastSortCol) ElseIf BitAND(GUICtrlRead($radio[1]), $GUI_CHECKED) = $GUI_CHECKED Then $lastSortCol=GUICtrlGetState($listview) _GUICtrlListView_SimpleSort($listview, $B_DESCENDING, $lastSortCol) EndIf Case $msg = $button $greenVal=$greenVal*5 GUICtrlSetData($listValues[2], "|"&$greenVal&"|"&$greenVal) Sleep(2000) If BitAND(GUICtrlRead($radio[0]), $GUI_CHECKED) = $GUI_CHECKED Then _GUICtrlListView_SortItems($listview, $lastSortCol) ElseIf BitAND(GUICtrlRead($radio[1]), $GUI_CHECKED) = $GUI_CHECKED Then $B_DESCENDING = NOT $B_DESCENDING _GUICtrlListView_SimpleSort($listview, $B_DESCENDING, $lastSortCol) EndIf EndSelect Until $msg = $GUI_EVENT_CLOSE _GUICtrlListView_UnRegisterSortCallBack($listview) EndFunc ;==>Example Steps to recreate: 1) Load above automation - note that column 2 is already sorted in Asc order) 2) Click the "Update green row and re-sort" button - this will update the green row, sleep for 2000, then trigger a sort You should notice that col 2 was initially sorted in Asc order, then after the sort was triggered it toggled to Desc (I want it to sort again in Asc order). Obviously, toggling Asc/Desc is the intended purpose (i.e. clicking on headings), but I'm wondering if an exception can be triggered for situations like this? I have also built in the ability to switch between sort methods ("SortItems" and "SimpleSort") to display the issue with formatting and "controlIds". (Edit: Click the headings of the listview to see what I mean) Many thanks in advance, Timbo Edited May 31, 2011 by timbo
timbo Posted May 31, 2011 Author Posted May 31, 2011 (edited) Hello again, Wouldn't you know it, 30 mintutes after I post I solved it... I can't say I entirely understand my solution, but it seems to work. I encourage anyone with the knowledge to clarify (or correct) my solution. I noticed that when you call "_GUICtrlListView_RegisterSortCallBack", it generates an array which is updated in subsequent "_GUICtrlListView_SortItems" calls. The array is shown below: $aListViewSortInfo[$iIndex][1] = $hWnd ; Handle/ID of listview $aListViewSortInfo[$iIndex][2] = _ DllCallbackRegister("__GUICtrlListView_Sort", "int", "int;int;hwnd") ; Handle of callback $aListViewSortInfo[$iIndex][3] = -1 ; $nColumn $aListViewSortInfo[$iIndex][4] = -1 ; nCurCol $aListViewSortInfo[$iIndex][5] = 1 ; $nSortDir $aListViewSortInfo[$iIndex][6] = -1 ; $nCol $aListViewSortInfo[$iIndex][7] = 0 ; $bSet $aListViewSortInfo[$iIndex][8] = $fNumbers ; Treat as numbers? $aListViewSortInfo[$iIndex][9] = $fArrows ; Use arrows in the header of the columns? $aListViewSortInfo[$iIndex][10] = $hHeader ; Handle to the Header When "SortItems" is called the array item "$aListViewSortInfo[$iIndex][7]" gets set to 0, I have no idea what that does, but if you stop that from happening, it won't toggle the Asc/Desc. So, my poorly designed solution consists on the following: 1) Setup the usual sorting methods for your listview 2) Copy the "_GUICtrlListView_SortItems" function from the "GuiListView.au3" include, paste it into your own script, and change the function name to avoid a conflict 3) Delete (or comment out) the reference to $aListViewSortInfo[$iIndex][7] = 0 ; $bSet 4) Call the usual "_GUICtrlListView_SortItems" on listview heading clicks, and call the renamed function when calling from your own function Hope this helps others, I also hope I haven't made a rookie mistake by incorrectly updating code (of which I have no clue on how it works). (Edit: Decided to add my modified function below:) expandcollapse popupFunc _GUICtrlListView_SortItems_NoToggle($hWnd, $iCol, $nSortDir=0) ;$nSortDir: 1=Ascending, -1=Descending, 0=Current Local $iRet, $iIndex, $pFunction, $hHeader, $iFormat If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) For $x = 1 To $aListViewSortInfo[0][0] If $hWnd = $aListViewSortInfo[$x][1] Then $iIndex = $x ExitLoop EndIf Next $pFunction = DllCallbackGetPtr($aListViewSortInfo[$iIndex][2]) ; get pointer to call back $aListViewSortInfo[$iIndex][3] = $iCol ; $nColumn = column clicked If $nSortDir<>0 Then $aListViewSortInfo[$iIndex][5] = $nSortDir ; $nSortDir $aListViewSortInfo[$iIndex][4] = $aListViewSortInfo[$iIndex][6] ; nCurCol = $nCol $iRet = _SendMessage($hWnd, $LVM_SORTITEMS, $hWnd, $pFunction, 0, "hwnd", "ptr") If $iRet <> 0 Then If $aListViewSortInfo[$iIndex][9] Then ; Use arrow in header $hHeader = $aListViewSortInfo[$iIndex][10] For $x = 0 To _GUICtrlHeader_GetItemCount($hHeader) - 1 $iFormat = _GUICtrlHeader_GetItemFormat($hHeader, $x) If BitAND($iFormat, $HDF_SORTDOWN) Then _GUICtrlHeader_SetItemFormat($hHeader, $x, BitXOR($iFormat, $HDF_SORTDOWN)) ElseIf BitAND($iFormat, $HDF_SORTUP) Then _GUICtrlHeader_SetItemFormat($hHeader, $x, BitXOR($iFormat, $HDF_SORTUP)) EndIf Next $iFormat = _GUICtrlHeader_GetItemFormat($hHeader, $iCol) If $aListViewSortInfo[$iIndex][5] = 1 Then ; ascending _GUICtrlHeader_SetItemFormat($hHeader, $iCol, BitOR($iFormat, $HDF_SORTUP)) Else ; descending _GUICtrlHeader_SetItemFormat($hHeader, $iCol, BitOR($iFormat, $HDF_SORTDOWN)) EndIf EndIf EndIf Return $iRet <> 0 EndFunc ;==>_GUICtrlListView_SortItems_NoToggle I added the variable "$nSortDir" to the function call which allows you to specify Asc/Desc (note: 1=Asc, -1=Desc, 0=Current). Regards, Timbo Edited May 31, 2011 by timbo
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now