czardas Posted May 25, 2016 Posted May 25, 2016 (edited) With a listview control, when I drag a column to a new location, I can no longer loop through the columns in ascending sequence. I would like to set the new column order as an ascending numeric sequence, so that I can loop through the columns normally (after dragging). Is there a way to force the ListView control to forget/reset the order of columns. The alternative is to implement complicated loop sequences and unwieldly code, which is painful and hell to debug. Edit: What I've tried: _GUICtrlListView_SetColumnOrderArray() moves the columns back to their original locations (that's not it ). I can't set the column number as an attribute - the method appears to be unsupported. It looks like I'm just going to have to track the column positions (all the time) and factor that in for every action taken which will probably add about 5% bloat to my project. Pity! Edited May 25, 2016 by czardas operator64 ArrayWorkshop
BrewManNH Posted May 25, 2016 Posted May 25, 2016 You could use _GUICtrlListView_GetColumnOrderArray and loop through the array instead. pixelsearch and czardas 2 If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag GudeHow to ask questions the smart way! I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from. Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays. - ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script. - Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label. - _FileGetProperty - Retrieve the properties of a file - SciTE Toolbar - A toolbar demo for use with the SciTE editor - GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI. - Latin Square password generator
czardas Posted May 25, 2016 Author Posted May 25, 2016 (edited) Yes and that would have to be done for every change made to the listview, if dragging columns is allowed. That's where the 5% unwieldy code comes from. This is for an undo buffer I'm intending to implement, so the code is quite complicated already. It seems I have no alternative other than to than to do it your way, but I was hoping I might be able to simplify the code. I implemented a similar tracking process recently with TagSort, so I can do it using the same idea: I just hate bloat for the sake of a simple reset feature not existing (apparently). Sadly, loop code is no longer straight forward (having to always refer to a key). On the other hand there are also some advantages to be gained from this implementation. I would just like to choose when I need to track something and when tracking is not needed. It's not a big deal. Edited May 25, 2016 by czardas operator64 ArrayWorkshop
LarsJ Posted May 26, 2016 Posted May 26, 2016 (edited) What do you mean exactly by looping through the columns in ascending sequence? Do you mean to loop through the columns from the leftmost column to the rightmost column? But this is not the usual opinion (at least not the MicroSoft opinion) of running through the columns in ascending order. The normal way to loop through the columns in ascending sequence and the way which is supported by MicroSoft code in the header and the listview controls is to use $iSubItem from $iSubItem = 0 to $iSubItem = $iColumns - 1. Looping through the columns in this way works for a large majority of all listview commands whether the columns have been dragged to other positions or not. $iSubItem always refers to the same column whether the columns have been rearranged or not. If two or more columns have been dragged to other positions the sequence from $iSubItem = 0 to $iSubItem = $iColumns - 1 does of course not match the left to right order of the columns. If you insists of looping through the columns in a left to right order (which changes every time the columns are rearranged) you need to use _GUICtrlListView_GetColumnOrderArray. But don't do that. You should use $iSubItem to loop through the columns. _GUICtrlListView_GetSubItemRect is an example of a command in which there is a problem when columns have been rearranged. It only works for $iSubItem > 0. For $iSubItem = 0 you must use _GUICtrlListView_GetItemRect. In this example I have only used _GUICtrlListView_GetSubItemRect. That's the reason why it doesn't work for the column with $iSubItem = 0, if that column has been dragged to a new position. I'll fix it. Conclusion: If you use $iSubItem to keep track of columns, you can usually add the $LVS_EX_HEADERDRAGDROP extended style to support rearranging of columns without changing any code at all. Edited May 26, 2016 by LarsJ Conclusion czardas and pixelsearch 1 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
czardas Posted May 26, 2016 Author Posted May 26, 2016 (edited) 46 minutes ago, LarsJ said: $iSubItem always refers to the same column whether the columns have been rearranged or not. Yes and that has merit, but it also imposes some restrictions. When we are in the business of manipulating data within a listview, writing an undo/redo buffer makes sense and presents certain challenges. The user is interacting with the control (not Microsoft) and they wouldn't be dragging columns to different positions simply for fun. To write consistent undo history code, one has to decide on the main architecture and try to stick with the same coherent method throughout. Starting with the premise that a table can always be read from top left to bottom right in an ascending sequence is a very good design concept. Thanks for the tips, I will look at the functions you mention. I wondered about your implementation. 46 minutes ago, LarsJ said: That's the reason why it doesn't work for the column with $iSubItem = 0, if that column has been dragged to a new position. I'll fix it. I have already written a patch which forces the first row to always hide itself before the user gets time to interact with it. If by some fluke the user somehow manages to drag the column at lightning speed, it instantly snaps back before the user gets chance to click anywhere else. It might be possible to get around these prevention measures using a script, but that's not particularly important. Edited May 26, 2016 by czardas operator64 ArrayWorkshop
pixelsearch Posted December 23, 2019 Posted December 23, 2019 (edited) On 5/25/2016 at 4:27 PM, BrewManNH said: You could use _GUICtrlListView_GetColumnOrderArray and loop through the array instead On 5/26/2016 at 7:49 AM, LarsJ said: Conclusion: If you use $iSubItem to keep track of columns, you can usually add the $LVS_EX_HEADERDRAGDROP extended style to support rearranging of columns without changing any code at all. This topic is important. I'm resurrecting it after 3.5 years to provide a runnable script that shows how to keep looping through listview columns from left to right, even after you made a mess by scrambling all listview headers, dragging & dropping them anywhere. Thanks to LarsJ above and another post of BrewManNH, here. expandcollapse popup; updated 23 Dec. 2019 - listview draggable headers => keep scrolling left to right #include <GUIConstantsEx.au3> #include <GuiListView.au3> #include <WindowsConstants.au3> #include <WinAPIvkeysConstants.au3> Global $g_aCol[0], $g_bEndDrag = False, $g_hHeader, $g_hListView Global $g_idDummy_Dbl_Click, $g_idListView, $g_iSubItem = -1, $g_iItem = -1 Example() ;============================================ Func Example() Local $hGUI = GUICreate("Wandering through ListView (901f #3)", 460, 500, -1, -1, _ BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX, $WS_THICKFRAME)) $g_idListView = GUICtrlCreateListView _ (" Col 0 | Col 1| Col 2| Col 3| Col 4|" & _ " Col 5| Col 6| Col 7| Col 8| Col 9", 15, 15, 430, 470) $g_hListView = GuiCtrlGetHandle($g_idListView) $g_hHeader = _GUICtrlListView_GetHeader($g_idListView) $g_aCol = _GUICtrlListView_GetColumnOrderArray($g_idListView) _GUICtrlListView_SetExtendedListViewStyle($g_idListView, _ BitOR($LVS_EX_HEADERDRAGDROP, $LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE, $LVS_EX_GRIDLINES)) _GUICtrlListView_SetCallBackMask($g_idListView, 4) ; "disable state information about focused item" (LarsJ) . Can be useful (keep that line) Local $sRow For $iRow = 0 To 99 $sRow = StringFormat("%2s", $iRow) GUICtrlCreateListViewItem( _ "Row " & $sRow & " / Col 0 |" & "Row " & $sRow & " / Col 1 |" & _ "Row " & $sRow & " / Col 2|" & "Row " & $sRow & " / Col 3 |" & _ "Row " & $sRow & " / Col 4|" & "Row " & $sRow & " / Col 5 |" & _ "Row " & $sRow & " / Col 6|" & "Row " & $sRow & " / Col 7 |" & _ "Row " & $sRow & " / Col 8|" & "Row " & $sRow & " / Col 9", $g_idListView) Next $g_idDummy_Dbl_Click = GUICtrlCreateDummy() Local $idDummy_Enter = GUICtrlCreateDummy() Local $aAccelKeys[1][2] = [["{ENTER}", $idDummy_Enter]] GUISetAccelerators($aAccelKeys) GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") GUISetState(@SW_SHOW) While 1 If $g_bEndDrag Then $g_aCol = _GUICtrlListView_GetColumnOrderArray($g_hListView) $g_bEndDrag = False EndIf Switch GUIGetMsg() Case $GUI_EVENT_CLOSE GUIDelete($hGUI) Exit Case $g_idDummy_Dbl_Click Horizontal_Scroll() MsgBox($MB_TOPMOST, "Double-click activated cell", _ "Row " & $g_iItem & " / Col " & $g_iSubItem) Case $idDummy_Enter Horizontal_Scroll() If _WinAPI_GetFocus() = $g_hListView And $g_iItem > -1 Then MsgBox($MB_TOPMOST, "Enter activated cell", _ "Row " & $g_iItem & " / Col " & $g_iSubItem) EndIf EndSwitch WEnd EndFunc ;==>Example ;============================================ Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $wParam Local $tNMHDR, $hWndFrom, $iIDFrom, $iCode $tNMHDR = DllStructCreate($tagNMHDR, $lParam) $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom") $iCode = DllStructGetData($tNMHDR, "Code") Static $bMouseDown = False Switch $hWndFrom Case $g_hListView Switch $iCode Case $NM_CUSTOMDRAW Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam) Local $iDrawStage = DllStructGetData($tCustDraw, "dwDrawStage") If $iDrawStage = $CDDS_PREPAINT Then Return $CDRF_NOTIFYITEMDRAW If $iDrawStage = $CDDS_ITEMPREPAINT Then Return $CDRF_NOTIFYSUBITEMDRAW Local $iItem = DllStructGetData($tCustDraw, "dwItemSpec") Local $iSubItem = DllStructGetData($tCustDraw, "iSubItem") Local $iColor = 0xFF000000 ; this is $CLR_DEFAULT in ColorConstants.au3 If $iItem = $g_iItem And $iSubItem = $g_iSubItem Then $iColor = 0xFFFFC0 ; light blue for 1 subitem (BGR) EndIf DllStructSetData($tCustDraw, "clrTextBk", $iColor) Return $CDRF_NEWFONT Case $LVN_KEYDOWN If $bMouseDown Or $g_iItem = -1 Then Return 1 ; don't process ; ===== ; https://www.autoitscript.com/forum/topic/195016-lvn_keydowntagnmlvkeydown-does-not-work-in-64-bit Local Static $tagNMLVKEYDOWN = $tagNMHDR & ";align 2;word VKey;uint Flags" ; mandatory Local as Global Const in StructureConstants.au3 ; ===== Local $tInfo = DllStructCreate($tagNMLVKEYDOWN, $lParam) Local $iVK = DllStructGetData($tInfo, "VKey") Switch $iVK Case $VK_RIGHT Local $iCol = _ArraySearch($g_aCol, $g_iSubItem, 1) If $iCol < $g_aCol[0] Then $g_iSubItem = $g_aCol[$iCol + 1] Horizontal_Scroll() _GUICtrlListView_RedrawItems($g_hListview, $g_iItem, $g_iItem) EndIf Case $VK_LEFT Local $iCol = _ArraySearch($g_aCol, $g_iSubItem, 1) If $iCol > 1 Then $g_iSubItem = $g_aCol[$iCol - 1] Horizontal_Scroll() _GUICtrlListView_RedrawItems($g_hListview, $g_iItem, $g_iItem) EndIf Case $VK_SPACE; don't process spacebar (it would select the whole row) Return 1 EndSwitch Case $NM_RELEASEDCAPTURE $bMouseDown = True Local $iItemSave = $g_iItem Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView) $g_iItem = $aHit[0] $g_iSubItem = $aHit[1] If $g_iItem = -1 And $iItemSave > -1 Then _GUICtrlListView_RedrawItems($g_hListView, $iItemSave, $iItemSave) EndIf Case $LVN_ITEMCHANGED Local $tInfo = DllStructCreate($tagNMLISTVIEW, $lParam) Local $iNewState = DllStructGetData($tInfo, "NewState") Switch $iNewState Case BitOr($LVIS_FOCUSED, $LVIS_SELECTED) $g_iItem = DllStructGetData($tInfo, "Item") _GUICtrlListView_SetItemSelected($g_hListView, $g_iItem, False) EndSwitch Case $NM_CLICK, $NM_RCLICK $bMouseDown = False Case $NM_DBLCLK $bMouseDown = False If $g_iItem > -1 Then GUICtrlSendToDummy($g_idDummy_Dbl_Click) EndSwitch Case $g_hHeader Switch $iCode Case $HDN_ENDDRAG ; sent by a header control when a drag operation has ended on one of its items $g_bEndDrag = True Return False ; allow the control to automatically place and reorder the item EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY ;============================================ Func Horizontal_Scroll() Local $iWidth = _GUICtrlListView_GetColumnWidth($g_hListView, $g_iSubItem) If $g_iSubItem Then Local $aRect = _GUICtrlListView_GetSubItemRect($g_hListView, $g_iItem, $g_iSubItem) Else ; column 0 needs _GUICtrlListView_GetItemRect() in case it has been dragged elsewhere (LarsJ) Local $aRect = _GUICtrlListView_GetItemRect($g_hListView, $g_iItem, 2) ; 2 = bounding rectangle of the item text $aRect[0] -= 4 ; adjust coords (tested) EndIf Local $aClientSize = WinGetClientSize($g_hListView) ; including headers, excluding both scrollbars (if any), great ! If $iWidth >= $aClientSize[0] Then $iWidth = $aClientSize[0] If $aRect[0] <> 0 Then _GUICtrlListView_Scroll($g_idListView, $aRect[0], 0) EndIf Else If $aRect[0] < 0 Then _GUICtrlListView_Scroll($g_idListView, $aRect[0], 0) Else If $aRect[0] + $iWidth > $aClientSize[0] Then _GUICtrlListView_Scroll($g_idListView, $aRect[0] + $iWidth - $aClientSize[0], 0) EndIf EndIf EndIf EndFunc ;==>Horizontal_Scroll Czardas seemed a bit disappointed when LarsJ wrote : "But this is not the usual opinion (at least not the MicroSoft opinion) of running through the columns in ascending order." As Czardas, I don't understand why this native left-to-right looping is suddenly broken as soon as you drag a listview column out of its initial place. Don't we do it in Excel since day 1 ? When you do same in Excel (dragging a column at another place) it doesn't break at all the left-to-right looping, then why should it be different in a listview control, when you're allowed to move columns at your wish ? I may be mistaken but the answer could be that, natively, you're not supposed to scroll horizontally cell by cell in a listview control (as you do in Excel), that could be the reason why Microsoft didn't care about the scrolling break from left-to-right when you drag a column in listview. But of course, they carefully change all indexes of the items when using the LVM_SORTITEMSEX message... because you natively scroll vertically by row (it would have been fun if they didn't change the vertical items indexes, happy vertical scrolling !) If you consider that moving a column means "a new column horizontal sort", then the left-to-right scroll shouldn't have been broken. Anyway, many thanks guys for your explanations I already have included this important new functionality to my script "CSV file editor", I just have to rewrite a few parts of it (for example, the exported csv file should probably be created according to the new columns order... or not, I'll have to think about it) Edited December 23, 2019 by pixelsearch "I think you are searching a bug where there is no bug... don't listen to bad advice."
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