Lion66 Posted May 26, 2021 Share Posted May 26, 2021 34 minutes ago, Norm73 said: I couldn't change the column size of the virtual ListView. This specifically prohibits the resizing with the hands. There was a bug with a second column update. See earlier in this thread. @pixelsearch. Thank you for your excellent example. But the following happens: Version 2K and an example of align columns from Larsj are made without using WM_DRAWITEM. If leave a column selection through a combobox and remove options the right mouse click, can even use the standard context menu. But in your last versions 2m and 2p, WM_DRAWITEM returned again and it does (for us nonprofessional) join together all functionality. 🙄 Do you have the opportunity to combine these functions, and preferably based on the script without WM_DRAWITEM. Many thanks. Norm73 1 Link to comment Share on other sites More sharing options...
pixelsearch Posted May 27, 2021 Share Posted May 27, 2021 Hi, version "2q" below includes LarsJ column align. It is based on WM_DRAWITEM and it will be my last update for the moment. expandcollapse popup#include <ComboConstants.au3> #include <EditConstants.au3> #include <GuiListView.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include "RandomArray.au3" ; LarsJ #include "DrawItem.au3" ; " Opt("MustDeclareVars", 1) Global $g_iRows = 1000, $g_iCols = 6, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60] Global $g_aColAligns[$g_iCols] = [$HDF_CENTER, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT] ; $HDF_LEFT, $HDF_RIGHT, $HDF_CENTER Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows Global $g_aArray, $g_aSubArray, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]") Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String" Global $fListViewHasFocus = 1 ; trying this for now, we'll see... Example() Func Example() ; Generate array & one index _Generate_All($g_aArray) $g_aSubArray = $g_aArray $g_tDefaultIndex = $g_tIndex ; Create GUI $g_hGui = GUICreate("Virtual ListView + match 1 or All columns + Align (2q)", 630 + 20, 788 + 30 + 20) ; Create Edit control (search) + dummy control Local $idEdit = GUICtrlCreateEdit("", 120, 10, 305, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL)) $g_hEdit = GUICtrlGetHandle($idEdit) $g_idEditDummy = GUICtrlCreateDummy() ; Create ComboBox control (how to search : RegEx or Normal ?) Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable) GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow) ; Create ComboBox control (column where to search, possible All) + dummy control GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER)) $g_idComboCol = GUICtrlCreateCombo("All", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) Local $sSearchCol = "All" ; default column where to search (changeable) => becomes $g_iSearchCol $g_iSearchCol = ($sSearchCol == "All" ? - 1 : Number($sSearchCol)) ; - 1 means search All columns Local $iSearchColPrev = $g_iSearchCol For $i = 0 To $g_iCols - 1 GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol) Next $g_idComboColDummy = GUICtrlCreateDummy() ; Create Label control (number of matching results) Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN)) ; Create ListView $g_idListView = GUICtrlCreateListView("", 10, 40, 630, 788, BitOr($LVS_OWNERDATA, $LVS_OWNERDRAWFIXED), $WS_EX_CLIENTEDGE) _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOr($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT)) $g_hListView = GUICtrlGetHandle($g_idListView) $g_hHeader = _GUICtrlListView_GetHeader($g_idListView) For $i = 0 To $g_iCols - 1 _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i]) _GUICtrlHeader_SetItemFormat($g_hHeader, $i, $HDF_STRING + $g_aColAligns[$i]) Next ; No ListView column resizing by dragging header dividers (LarsJ) ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _ DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING ) ; Create Marker (an orange line placed above the header of the column being searched) $g_idMarker = GUICtrlCreateLabel("", 0, 0) GUICtrlSetBkColor(-1, 0xFFA060) ; orange _MoveMarker($g_iSearchCol) _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol) ; Sorting information $g_iSortDir = $HDF_SORTUP Local $iSortCol = -1, $iSortColPrev = -1 ; Register message handlers GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") ; for LV header only GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ; Sets the virtual number of items in a virtual list-view control GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $g_idComboCol, $g_idComboColDummy, $idSearchHow $sSearchCol = GUICtrlRead($g_idComboCol) $g_iSearchCol = ($sSearchCol == "All" ? - 1 : Number($sSearchCol)) ; - 1 means search All columns $sSearchHow = GUICtrlRead($idSearchHow) Select Case $g_iSearchCol <> $iSearchColPrev _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase $iSearchColPrev = $g_iSearchCol Case $sSearchHow <> $sSearchHowPrev $sSearchHowPrev = $sSearchHow Case Else ; no change in both Combo controls (same search column, same search way) ContinueLoop EndSelect ContinueCase Case $g_idEditDummy _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + ($iSortCol > -1 ? $g_aColAligns[$iSortCol] : 0)) $g_sSearch = GUICtrlRead($idEdit) $g_tIndex = $g_tDefaultIndex If $g_sSearch = "" Then ; Empty search string, display all rows $g_aSubArray = $g_aArray $g_iSearch = $g_iRows Else ; Find rows matching the search string $g_iSearch = 0 If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1") EndIf ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf) If $g_iSearchCol = - 1 Then ; - 1 means search All columns For $i = 0 To $g_iRows - 1 For $j = 0 To $g_iCols - 1 If StringRegExp($g_aArray[$i][$j], "(?i)" & $g_sSearch) Then For $k = 0 To $g_iCols - 1 $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k] Next $g_iSearch += 1 ContinueLoop 2 EndIf Next Next Else ; Search only in 1 column For $i = 0 To $g_iRows - 1 If StringRegExp($g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch) Then For $j = 0 To $g_iCols - 1 $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j] Next $g_iSearch += 1 EndIf Next EndIf ; Delete eventual temporary subindexes For $i = 0 To $g_iCols - 1 If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String" Next EndIf GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows")) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0) _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT) Case $g_idListView ; Sort $iSortCol = GUICtrlGetState($g_idListView) If $iSortCol <> $iSortColPrev Then _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING + ($iSortColPrev > -1 ? $g_aColAligns[$iSortColPrev] : 0)) EndIf ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] OR $g_aIndex[$iSortCol] If GUICtrlRead($idEdit) Then _UpdateIndex($g_aIndexTemp, $iSortCol) Else _UpdateIndex($g_aIndex, $iSortCol) EndIf $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP)) _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_aColAligns[$iSortCol] + $g_iSortDir) GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0) $iSortColPrev = $iSortCol Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?) _MoveMarker($g_iSearchCol) EndSwitch WEnd ; Cleanup GUIDelete($g_hGui) EndFunc ;==>Example ;======================================================================== Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam ) ; Display items in an owner drawn ListView Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE ) Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0xFFFF00 ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0x00FFFF ) ; Yellow and cyan, BGR Local Static $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE ) ; We can optimize code by removing Switch statements because the ListView is the only ownerdrawn control and only $ODA_DRAWENTIRE actions are present Local $tDrawItem = DllStructCreate( $tagDRAWITEM, $lParam ), $itemID = DllStructGetData( $tDrawItem, "itemID" ), $iState = DllStructGetData( $tDrawItem, "itemState" ), $hDC = DllStructGetData( $tDrawItem, "hDC" ), $sItemText ; Loop through columns ($i is the column index) For $i = 0 To $g_iCols - 1 ; Subitem rectangle DllStructSetData( $tRect, 2, $i ) ; Top DllStructSetData( $tRect, 1, $LVIR_BOUNDS ) ; Left GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $itemID, $pRect ) DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 3 ) ; Left margin ; If $i = 0 (first column), the rectangle is calculated for the entire listview item. ; Compensate for this by setting the width of the rectangle to the width of the first column. ; Before that, if item is selected, fill the entire listview item with the highlight background color. If $i = 0 Then If BitAND( $iState, $ODS_SELECTED ) Then DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $fListViewHasFocus = 1 ? $hBrushHighLight : $hBrushButtonFace ) ; _WinAPI_FillRect DllStructSetData( $tRect, 3, DllStructGetData( $tRect, 1 ) + GUICtrlSendMsg( $g_idListView, $LVM_GETCOLUMNWIDTH, 0, 0 ) ) EndIf ; Retrieve subitem text If $g_iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP $sItemText = $g_aSubArray[$g_tIndex.arr($itemID + 1)][$i] Else $sItemText = $g_aSubArray[$g_tIndex.arr($g_iSearch - $itemID)][$i] EndIf ; Subitem rectangle for right and center aligned columns If $g_aColAligns[$i] = $HDF_RIGHT Or $g_aColAligns[$i] = $HDF_CENTER Then DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 Switch $g_aColAligns[$i] Case $HDF_RIGHT DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 ) Case $HDF_CENTER DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + ( DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tRect, "Left" ) - DllStructGetData( $tSize, "X" ) ) / 2 - 3 ) EndSwitch EndIf ; Subitem text color DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", BitAND( $iState, $ODS_SELECTED ) ? $fListViewHasFocus = 1 ? 0xFFFFFF : 0x000000 : 0x000000 ) ; _WinAPI_SetTextColor ; Draw subitem text DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText ; Skip column if search for 1 column only and $i is not the searched column If $g_iSearchCol > - 1 And $i <> $g_iSearchCol Then ContinueLoop ; > - 1 means search for 1 column (0, 1, 2...) ; Mark matching substring if found in this column If $g_sSearch Then Local $sMatch = StringRegExp( $sItemText, "(?i)" & $g_sSearch, 1 ) If Not @error Then ; match found Local $extended = @extended, $iLen = StringLen( $sMatch[0] ) ; Rectangle for matching substring DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) ; Fill rectangle with yellow or cyan (selected) background color DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 1 ) ; Top margin DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", BitAND( $iState, $ODS_SELECTED ) ? $hBrushCyan : $hBrushYellow ) ; _WinAPI_FillRect ; Draw matching substring in rectangle DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) - 1 ) ; Top margin DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText EndIf EndIf Next Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc ;======================================================================== Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam) Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam) Switch HWnd(DllStructGetData($tNMHEADER, "hWndFrom")) Case $g_hHeader Local $iCode = DllStructGetData($tNMHEADER, "Code") Switch $iCode Case $NM_RCLICK Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView) ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header) If $aHit[1] > - 1 Then ; valid column GUICtrlSetData($g_idComboCol, $aHit[1]) GUICtrlSendToDummy($g_idComboColDummy) EndIf EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY ;======================================================================== Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) Local $hWndFrom = $lParam Local $iCode = BitShift($wParam, 16) ; High word Switch $hWndFrom Case $g_hEdit Switch $iCode Case $EN_CHANGE GUICtrlSendToDummy($g_idEditDummy) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND ;======================================================================== Func _Generate_All(ByRef $g_aArray) ConsoleWrite("$g_iRows = " & $g_iRows & " $g_iCols = " & $g_iCols & @CRLF) Local $hTimer = TimerInit() $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz") ; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters For $i = 0 To $g_iRows - 1 $g_tIndex.arr($i + 1) = $i Next ConsoleWrite("Generating array & one index = " & TimerDiff($hTimer) & @CRLF & @CRLF) EndFunc ;==>_Generate_All ;======================================================================== Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows) Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]") Local $pIndex = DllStructGetPtr($tIndex) Local Static $hDll = DllOpen("kernel32.dll") Local Static $hDllComp = DllOpen("shlwapi.dll") Local $lo, $hi, $mi, $r ; Sorting by one column For $i = 1 To $iRows - 1 $lo = 0 $hi = $i - 1 Do $mi = Int(($lo + $hi) / 2) $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0] Switch $r Case -1 $hi = $mi - 1 Case 1 $lo = $mi + 1 Case 0 ExitLoop EndSwitch Until $lo > $hi DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4) DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1)) Next Return $tIndex EndFunc ;==>_SortArrayStruct ;======================================================================== Func _UpdateIndex(ByRef $aIndex, $iCol) If VarGetType($aIndex[$iCol]) = "DLLStruct" Then $g_tIndex = $aIndex[$iCol] Else $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_iSearch) $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++) EndIf EndFunc ;==>_UpdateIndex ;======================================================================== Func _MoveMarker($iCol) If $iCol = - 1 Then ; All columns ControlMove($g_hGui, "", $g_idMarker, 10, 40 - 3, 630, 3) ; 10 / 40 / 630 are LV coords Else ; 1 column (0, 1, 2...) Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ControlMove($g_hGui, "", $g_idMarker, 10 + $aRect[0], 40 - 3, $aRect[2] - $aRect[0] + 1, 3) EndIf EndFunc ;==>_MoveMarker You should be able to combine the present functionalities with $NM_CUSTOMDRAW instead of $WM_DRAWITEM, not forgetting the special context menu found in this thread, and not forgetting Norm73's wish to immediately scroll on 1st match, for example like in the pic below (2 first non-matching lines left visible on purpose) . The pic below is not a script, it's just the way it could appear : @Norm73 unfortunately _GUICtrlListView_EnsureVisible (as indicated in one of your last post) isn't enough to do what you want. It sometimes display the requested item at the bottom of the screen (if you were selecting an item placed below it) LVM_GETTOPINDEX is useful to retrieve the index of the topmost visible item, but unfortunately there is no "LVM_SETTOPINDEX" to force an item to be placed at topmost position. After some searches here & there, I tested a way that can do it and it seems to work fine. Here is the code in case anyone is interested : ;~ ; It works !!! ;~ Local $iRow1stMatch = 200 ;~ Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) ;~ Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($g_idListView, True) ; only once ;~ _GUICtrlListView_Scroll($g_idListView, 0, ($iRow1stMatch - $iGetTopIndex) * $iGetItemSpacingY) Now it's time to rest, no more AutoIt during several days. Good luck guys and see you one of these days Gianni, Norm73 and Lion66 1 2 Link to comment Share on other sites More sharing options...
Lion66 Posted May 27, 2021 Share Posted May 27, 2021 Wonderful! Thank you very much and have a good rest! 🙃 Link to comment Share on other sites More sharing options...
Norm73 Posted May 28, 2021 Share Posted May 28, 2021 (edited) Thank LarsJ and pixelsearch for helping beginners. For two weeks I've been struggling with the fact that this will work as a universal UDF function for all of my ListViews. It would be a wrong way to always create the same function for every list. If possible, please post this version as well: Edited May 28, 2021 by Norm73 Link to comment Share on other sites More sharing options...
ldub Posted May 28, 2021 Share Posted May 28, 2021 Yes, please ! And, if possible, more versions in a ZIP file. I have not managed to run some of them because they were incomplete. In any case, thank you very much to all. Link to comment Share on other sites More sharing options...
Norm73 Posted May 29, 2021 Share Posted May 29, 2021 (edited) This is my attempt to make the pixselsearch script a little more flexible so that it can be used in any GUI with virtual ListView. Realized functions: Registration Search Sorting Selected row text Unregistration Edited June 12, 2021 by Norm73 Gianni and pixelsearch 2 Link to comment Share on other sites More sharing options...
ldub Posted May 30, 2021 Share Posted May 30, 2021 Helpfull @Norm73 Many Thanks Link to comment Share on other sites More sharing options...
Norm73 Posted June 9, 2021 Share Posted June 9, 2021 (edited) On 5/27/2021 at 8:15 AM, pixelsearch said: @Norm73 unfortunately _GUICtrlListView_EnsureVisible (as indicated in one of your last post) isn't enough to do what you want. It sometimes display the requested item at the bottom of the screen (if you were selecting an item placed below it) LVM_GETTOPINDEX is useful to retrieve the index of the topmost visible item, but unfortunately there is no "LVM_SETTOPINDEX" to force an item to be placed at topmost position. After some searches here & there, I tested a way that can do it and it seems to work fine. Here is the code in case anyone is interested : ;~ ; It works !!! ;~ Local $iRow1stMatch = 200 ;~ Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) ;~ Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($g_idListView, True) ; only once ;~ _GUICtrlListView_Scroll($g_idListView, 0, ($iRow1stMatch - $iGetTopIndex) * $iGetItemSpacingY) I've tried your example. It worked well too, that's why I incorporated your idea into my code so that anyone can use it if necessary. Here I tried to implement different search options using great examples from LarsJ and pixelsearch. Everything (seems) to be working fine, but I am bothered by one little thing that I cannot change myself. So I ask for help from more experienced people. The point is that the search is always performed in a "static" array and the search result is always sorted exactly like the array. For example, if I sorted the second column before searching, the sorting isn't preserved after the search. If a filter is used to search, ie unnecessary lines are deleted, this is usually not noticed and is not a problem. However, if the scrolling search is used, it is immediately noticeable that the sorting has been reset. Please help me to solve this problem so that the sorting would also be saved after the search. Edited June 12, 2021 by Norm73 Link to comment Share on other sites More sharing options...
pixelsearch Posted June 9, 2021 Share Posted June 9, 2021 (edited) Hi Norm73 It's not obvious to have both options (your combo "Scroll" and "Filter") in the same script, because it may require to work on different sub-arrays when you choose each option. Also the fact that we can sort on a column while searching in another column makes it hard too. Plus this "All" option in the Combo, all these permanent and temporary indexes etc... LarsJ himself splitted both options in 2 separate scripts (which were both very inspiring), named : 1) Incremental text search.au3 2) Show matching rows only A couple of days ago, I ended a script (named "2s") mixing both options as you will see below, but it still requires some testing. I didn't want to post it because it will be a bit hard to maintain and my health isn't going really well (eyes, ears... you name it) so I won't come on the Forum as frequently as I used to, Times are changing, unfortunately... Anyway, in case it may help you (or other users) here is the most interesting functionality in this version (a context menu allowing to display only matching rows, or all rows) . Pics being better than words : The context menu option "Right-click Headers" is checkable too. When checked, it allows to switch quickly from a search column to another. Now the full script "2s" : expandcollapse popup#include <ComboConstants.au3> #include <EditConstants.au3> #include <GuiListView.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include "RandomArray.au3" ; LarsJ #include "DrawItem.au3" ; " Opt("MustDeclareVars", 1) Global $g_iRows = 1000, $g_iCols = 6, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60] Global $g_aColAligns[$g_iCols] = [2, 1, 1, 1, 1, 1] ; $HDF_LEFT = 0, $HDF_RIGHT = 1, $HDF_CENTER = 2 Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows, $g_iLvItem = -1 Global $g_aArray, $g_aSubArray, $g_aSubArray2, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]") Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String" Global $g_idContextDummy, $g_hContextmenu, $g_iItem = -1, $g_iSubItem = -1, $g_bShowAllRows = False, $g_bRightClickHeaders = True Global $fListViewHasFocus = 1 ; trying this for now, we'll see... Example() Func Example() ; Generate array & one index _Generate_All($g_aArray) $g_aSubArray = $g_aArray $g_aSubArray2 = $g_aArray $g_tDefaultIndex = $g_tIndex ; Compute LV_Height based on 40 full visible rows, no additional vertical pixel. Local $iLV_Height = _ComputeLV_Height(40) ; only 1 param => LV got headers & border ConsoleWrite("Computed LV height = " & $iLV_Height & " pixels" & @crlf) ; Create GUI $g_hGui = GUICreate("Virtual ListView + Incr. Search + Show matching or All rows (2s)", 630 + 20, $iLV_Height + 30 + 20) ; Create Edit control (search) + dummy control Local $idEdit = GUICtrlCreateEdit("", 120, 10, 182, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL)) $g_hEdit = GUICtrlGetHandle($idEdit) $g_idEditDummy = GUICtrlCreateDummy() ; Create 2 Search Buttons (Prev / Next) & 2 Accelerator Keys (F3 / Shift+F3) Local $idPrev = GUICtrlCreateButton("Prev", 312, 10, 50, 20) Local $idNext = GUICtrlCreateButton("Next", 370, 10, 50, 20) GUICtrlSetState($idPrev, $GUI_DISABLE) GUICtrlSetState($idNext, $GUI_DISABLE) Local $aAccelKeys[2][2] = [ [ "{F3}", $idNext ], [ "+{F3}", $idPrev ] ] GUISetAccelerators($aAccelKeys) ; Create ComboBox control (how to search : RegEx or Normal ?) Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable) GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow) ; Create ComboBox control (column where to search) + dummy control GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER)) $g_idComboCol = GUICtrlCreateCombo("0", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) $g_iSearchCol = 0 ; default column where to search (changeable) Local $iSearchColPrev = $g_iSearchCol For $i = 1 To $g_iCols - 1 GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol) Next $g_idComboColDummy = GUICtrlCreateDummy() ; Create Label control (number of matching results) Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN)) ; Create ListView $g_idListView = GUICtrlCreateListView("", 10, 40, 630, $iLV_Height, BitOr($GUI_SS_DEFAULT_LISTVIEW, $LVS_OWNERDATA, $LVS_OWNERDRAWFIXED), $WS_EX_CLIENTEDGE) _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOr($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT)) $g_hListView = GUICtrlGetHandle($g_idListView) $g_hHeader = _GUICtrlListView_GetHeader($g_idListView) For $i = 0 To $g_iCols - 1 _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i]) _GUICtrlHeader_SetItemFormat($g_hHeader, $i, $HDF_STRING + $g_aColAligns[$i]) Next Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($g_idListView, True) ; No ListView column resizing by dragging header dividers (LarsJ) ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _ DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING ) ; Create Marker (an orange line placed above the header of the column being searched) $g_idMarker = GUICtrlCreateLabel("", 0, 0) GUICtrlSetBkColor(-1, 0xFFA060) ; orange _MoveMarker($g_iSearchCol) _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol) ; Create Context menu $g_idContextDummy = GUICtrlCreateDummy() Local $idContextMenu = GUICtrlCreateContextMenu($g_idContextDummy) Local $idContext_GetText = GUICtrlCreateMenuItem("Get text", $idContextMenu) GUICtrlCreateMenuItem("", $idContextMenu) Local $idContext_ShowAll = GUICtrlCreateMenuItem("Show all rows", $idContextMenu) Local $idContext_RightClickHeaders = GUICtrlCreateMenuItem("Right-click Headers", $idContextMenu) GUICtrlSetState(-1, 1) ; $GUI_CHECKED = 1 $g_hContextMenu = GuiCtrlGetHandle($idContextMenu) ; Sorting information $g_iSortDir = 0x0400 ; $HDF_SORTUP = 0x0400 Local $iSortCol = -1, $iSortColPrev = -1 ; Register message handlers GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ; Sets the virtual number of items in a virtual list-view control GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0) GUISetState(@SW_SHOW) ; Message loop Local $aSearch[$g_iRows], $iStart While 1 Switch GUIGetMsg() Case $g_idComboCol, $g_idComboColDummy, $idSearchHow $g_iSearchCol = GUICtrlRead($g_idComboCol) $sSearchHow = GUICtrlRead($idSearchHow) Select Case $g_iSearchCol <> $iSearchColPrev _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase $iSearchColPrev = $g_iSearchCol Case $sSearchHow <> $sSearchHowPrev $sSearchHowPrev = $sSearchHow Case Else ; no change in both Combo controls (same search column, same search way) ContinueLoop EndSelect ContinueCase Case $g_idEditDummy _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + ($iSortCol > -1 ? $g_aColAligns[$iSortCol] : 0)) $g_sSearch = GUICtrlRead($idEdit) $g_tIndex = $g_tDefaultIndex ; Sorting information (same as 1st launch, to avoid descending displays when all unsorted rows are shown) $g_iSortDir = 0x0400 ; $HDF_SORTUP = 0x0400 $iSortCol = -1 $iSortColPrev = -1 If $g_sSearch = "" Then ; Empty search string, display all rows $g_aSubArray = $g_aArray $g_aSubArray2 = $g_aArray $g_iSearch = $g_iRows GUICtrlSetState($idPrev, $GUI_DISABLE) GUICtrlSetState($idNext, $GUI_DISABLE) Else ; Find rows matching the search string $g_iSearch = 0 If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1") EndIf ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf) Dim $aSearch[$g_iRows] For $i = 0 To $g_iRows - 1 If StringRegExp($g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch) Then For $j = 0 To $g_iCols - 1 $g_aSubArray2[$g_iSearch][$j] = $g_aArray[$i][$j] Next $aSearch[$i] = 1 ; include row ($aSearch will serve if user wants to show all rows and click Next / Prec) $g_iSearch += 1 EndIf Next ; Delete eventual temporary subindexes For $i = 0 To $g_iCols - 1 If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String" Next $g_aSubArray = $g_bShowAllRows ? $g_aArray : $g_aSubArray2 GUICtrlSetState($idPrev, ($g_bShowAllRows And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSetState($idNext, ($g_bShowAllRows And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) EndIf GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows")) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT) Case $g_idListView ; Sort $iSortCol = GUICtrlGetState($g_idListView) If $iSortCol <> $iSortColPrev Then _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING + ($iSortColPrev > -1 ? $g_aColAligns[$iSortColPrev] : 0)) EndIf ; Set $g_tIndex + eventual update of $g_aIndex[$iSortCol] OR $g_aIndexTemp[$iSortCol] If $g_bShowAllRows Or $g_sSearch = "" Or $g_iSearch = $g_iRows Then _UpdateIndex($g_aIndex, $iSortCol) Else _UpdateIndex($g_aIndexTemp, $iSortCol) EndIf $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP)) _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_aColAligns[$iSortCol] + $g_iSortDir) GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) $iSortColPrev = $iSortCol ; Update $aSearch (eventually) + automatic display of 1st match (eventually too) If $g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows Then _Update_aSearch($aSearch, $iSortCol, $idPrev, $idNext) EndIf Case $idNext $iStart = $g_iLvItem = - 1 ? 0 : $g_iLvItem < $g_iRows - 1 ? $g_iLvItem + 1 : 0 While 1 ; The while loop is used to start all over when last row in the listview is reached For $i = $iStart To $g_iRows - 1 If $aSearch[$i] And $i > $g_iLvItem Then ExitLoop Next If $i < $g_iRows Then ExitLoop $g_iLvItem = -1 $iStart = 0 Wend If $g_iLvItem > - 1 Then _GUICtrlListView_SetItemSelected($g_idListView, $g_iLvItem, False, False) _GUICtrlListView_ClickItem($g_idListView, $i) If $iStart = 0 Then Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) _GUICtrlListView_Scroll($g_idListView, 0, ($i - $iGetTopIndex) * $iGetItemSpacingY) EndIf _GUICtrlListView_SetItemSelected($g_idListView, $i, True, True) Case $idPrev $iStart = $g_iLvItem = - 1 ? $g_iRows - 1 : $g_iLvItem > 0 ? $g_iLvItem - 1 : $g_iRows - 1 While 1 ; The while loop is used to start all over when first row in the listview is reached For $i = $iStart To 0 Step -1 If $aSearch[$i] And $i < $g_iLvItem Then ExitLoop Next If $i > -1 Then ExitLoop $g_iLvItem = $g_iRows $iStart = $g_iRows - 1 Wend If $g_iLvItem > - 1 Then _GUICtrlListView_SetItemSelected($g_idListView, $g_iLvItem, False, False) _GUICtrlListView_ClickItem($g_idListView, $i) If $iStart = 0 Then Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) _GUICtrlListView_Scroll($g_idListView, 0, ($i - $iGetTopIndex) * $iGetItemSpacingY) EndIf _GUICtrlListView_SetItemSelected($g_idListView, $i, True, True) Case $g_idContextDummy If $g_iItem > - 1 Then _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSubItem) GUICtrlSetState($idContext_GetText, $g_iItem = - 1 ? $GUI_DISABLE : $GUI_ENABLE) Local $iMenu_Choice = _TrackPopupMenu($g_hContextMenu, $g_hGui, MouseGetPos(0), MouseGetPos(1)) If $iMenu_Choice Then Switch $iMenu_Choice Case $idContext_GetText ; Get text Local $sGetText = _GetText($g_iItem, $g_iSubItem) MsgBox($MB_TOPMOST, "Context menu", "Row = " & $g_iItem & " Column = " & $g_iSubItem & " Text = " & $sGetText & @lf) Case $idContext_ShowAll ; Show all rows ? If BitAND(GUICtrlRead($idContext_ShowAll), 4) = 4 Then ; $GUI_UNCHECKED = 4 GUICtrlSetState($idContext_ShowAll, 1) ; $GUI_CHECKED = 1 $g_bShowAllRows = True $g_aSubArray = $g_aArray ; Set $g_tIndex + eventual update $g_aIndex[$iSortCol] If $iSortCol > - 1 Then _UpdateIndex($g_aIndex, $iSortCol) ; Update $aSearch (eventually) + automatic display of 1st match (eventually too) If $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows Then _Update_aSearch($aSearch, $iSortCol, $idPrev, $idNext) Else GUICtrlSetState($idContext_ShowAll, 4) ; $GUI_UNCHECKED = 4 $g_bShowAllRows = False $g_aSubArray = $g_aSubArray2 ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] If $iSortCol > - 1 Then _UpdateIndex($g_aIndexTemp, $iSortCol) ; No eventual update of $aSearch because $g_bShowAllRows = False (2 buttons Prev & Next are disable in this case) EndIf GUICtrlSetState($idPrev, ($g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSetState($idNext, ($g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) Case $idContext_RightClickHeaders ; Activate headers right-click ? If BitAND(GUICtrlRead($idContext_RightClickHeaders), 4) = 4 Then ; $GUI_UNCHECKED = 4 GUICtrlSetState($idContext_RightClickHeaders, 1) ; $GUI_CHECKED = 1 $g_bRightClickHeaders = True Else GUICtrlSetState($idContext_RightClickHeaders, 4) ; $GUI_UNCHECKED = 4 $g_bRightClickHeaders = False EndIf EndSwitch Endif Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?) _MoveMarker($g_iSearchCol) Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd ; Cleanup GUIDelete($g_hGui) EndFunc ;==>Example ;======================================================================== Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Display items in an owner drawn ListView (called once per item : no separate messages for each subitem +++) Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE ) Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0xFFFF00 ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0x00FFFF ) ; Yellow and cyan, BGR Local Static $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE ) Local $tDrawItem = DllStructCreate( $tagDRAWITEM, $lParam ), $itemID = DllStructGetData( $tDrawItem, "itemID" ), $iState = DllStructGetData( $tDrawItem, "itemState" ), $hDC = DllStructGetData( $tDrawItem, "hDC" ), $sItemText ; Loop through columns ($i is the column index) For $i = 0 To $g_iCols - 1 ; Subitem rectangle DllStructSetData( $tRect, 2, $i ) ; Top DllStructSetData( $tRect, 1, $LVIR_BOUNDS ) ; Left GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $itemID, $pRect ) DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 0 ) ; Left margin ; If $i = 0 (first column), the rectangle is calculated for the entire listview item. ; Compensate for this by setting the width of the rectangle to the width of the first column. ; Before that, if item is selected, fill the entire listview item with the highlight background color. If $i = 0 Then If BitAND( $iState, $ODS_SELECTED ) Then DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $fListViewHasFocus = 1 ? $hBrushHighLight : $hBrushButtonFace ) ; _WinAPI_FillRect DllStructSetData( $tRect, 3, DllStructGetData( $tRect, 1 ) + GUICtrlSendMsg( $g_idListView, $LVM_GETCOLUMNWIDTH, 0, 0 ) ) EndIf ; Retrieve subitem text If $g_iSortDir = 0x0400 Then ; $HDF_SORTUP = 0x0400 $sItemText = $g_aSubArray[$g_tIndex.arr($itemID + 1)][$i] Else $sItemText = $g_aSubArray[$g_tIndex.arr(($g_bShowAllRows ? $g_iRows : $g_iSearch) - $itemID)][$i] EndIf ; Subitem rectangle for right and center aligned columns If $g_aColAligns[$i] Then ; $HDF_LEFT = 0, $HDF_RIGHT = 1, $HDF_CENTER = 2 DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 Switch $g_aColAligns[$i] Case 1 ; $HDF_RIGHT DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 ) Case 2 ; $HDF_CENTER DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + ( DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tRect, "Left" ) - DllStructGetData( $tSize, "X" ) ) / 2 - 3 ) EndSwitch EndIf ; Subitem text color DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", BitAND( $iState, $ODS_SELECTED ) ? $fListViewHasFocus = 1 ? 0xFFFFFF : 0x000000 : 0x000000 ) ; _WinAPI_SetTextColor ; Draw subitem text DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText ; $i is column index, $g_iSearchCol is the search column ; Mark matching substring only if column index = search column If $i <> $g_iSearchCol Then ContinueLoop ; Matching substring? If $g_sSearch Then Local $sMatch = StringRegExp( $sItemText, "(?i)" & $g_sSearch, 1 ) If Not @error Then ; match found Local $extended = @extended, $iLen = StringLen( $sMatch[0] ) ; Rectangle for matching substring DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) ; Fill rectangle with yellow or cyan (selected) background color DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 1 ) ; Top margin DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", BitAND( $iState, $ODS_SELECTED ) ? $hBrushCyan : $hBrushYellow ) ; _WinAPI_FillRect ; Draw matching substring in rectangle DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) - 1 ) ; Top margin DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText EndIf EndIf Next Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc ;======================================================================== Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam) Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam) Switch HWnd(DllStructGetData($tNMHDR, "hWndFrom")) Case $g_hListView Switch DllStructGetData($tNMHDR, "Code") Case $LVN_ITEMCHANGED Local $tNMListView = DllStructCreate($tagNMLISTVIEW, $lParam) Local $iNewState = DllStructGetData($tNMListView, "NewState") If BitAND($iNewState, $LVIS_FOCUSED) Then $g_iLvItem = DllStructGetData($tNMListView, "Item") Case $NM_RCLICK Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $lParam) $g_iItem = DllStructGetData($tInfo, "Index") ;~ If $g_iItem > -1 Then ; valid row $g_iSubItem = DllStructGetData($tInfo, "SubItem") GUICtrlSendToDummy($g_idContextDummy) ;~ EndIf EndSwitch Case $g_hHeader Switch DllStructGetData($tNMHDR, "Code") Case $NM_RCLICK If $g_bRightClickHeaders Then Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView) ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header) If $aHit[1] > - 1 Then ; valid column GUICtrlSetData($g_idComboCol, $aHit[1]) GUICtrlSendToDummy($g_idComboColDummy) EndIf EndIf Case $HDN_ENDTRACKW ; keep it, in case someone needs it _MoveMarker(GUICtrlRead($g_idComboCol)) Case $HDN_DIVIDERDBLCLICKW ; keep it, in case someone needs it Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam) Local $iCol = DllStructGetData($tNMHEADER, "Item") _GUICtrlListView_SetColumnWidth($g_idListView, $iCol, $g_aWidths[$iCol]) ; initial size _MoveMarker(GUICtrlRead($g_idComboCol)) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY ;======================================================================== Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) Local $hWndFrom = $lParam Local $iCode = BitShift($wParam, 16) ; High word Switch $hWndFrom Case $g_hEdit Switch $iCode Case $EN_CHANGE GUICtrlSendToDummy($g_idEditDummy) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND ;======================================================================== Func _Generate_All(ByRef $g_aArray) ConsoleWrite("$g_iRows = " & $g_iRows & " $g_iCols = " & $g_iCols & @CRLF) $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz") ; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters For $i = 0 To $g_iRows - 1 $g_tIndex.arr($i + 1) = $i Next EndFunc ;==>_Generate_All ;======================================================================== Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows) Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]") Local $pIndex = DllStructGetPtr($tIndex) Local Static $hDll = DllOpen("kernel32.dll") Local Static $hDllComp = DllOpen("shlwapi.dll") Local $lo, $hi, $mi, $r ; Sorting by one column For $i = 1 To $iRows - 1 $lo = 0 $hi = $i - 1 Do $mi = Int(($lo + $hi) / 2) $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0] Switch $r Case -1 $hi = $mi - 1 Case 1 $lo = $mi + 1 Case 0 ExitLoop EndSwitch Until $lo > $hi DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4) DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1)) Next Return $tIndex EndFunc ;==>_SortArrayStruct ;======================================================================== Func _UpdateIndex(ByRef $aIndex, $iCol) If VarGetType($aIndex[$iCol]) = "DLLStruct" Then $g_tIndex = $aIndex[$iCol] Else $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_bShowAllRows ? $g_iRows : $g_iSearch) $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++) EndIf EndFunc ;==>_UpdateIndex ;======================================================================== Func _Update_aSearch(ByRef $aSearch, $iSortCol, $idPrev, $idNext) $g_iSearch = 0 Dim $aSearch[$g_iRows] For $i = 0 To $g_iRows - 1 If StringRegExp($g_aArray[$g_tIndex.arr($g_iSortDir = 0x0400 ? $i + 1 : $g_iRows - $i)][$g_iSearchCol], "(?i)" & $g_sSearch) Then $aSearch[$i] = 1 ; include row $g_iSearch += 1 EndIf Next If $g_iSearch And $iSortCol = $g_iSearchCol Then GUICtrlSetState($idPrev, $GUI_ENABLE) GUICtrlSetState($idNext, $GUI_ENABLE) $g_iLvItem = - 1 ; comment this line if 1st match not to be displayed on 1st row ControlClick($g_hGui, "", $idNext) EndIf EndFunc ;==>_Update_aSearch ;======================================================================== Func _MoveMarker($iCol) Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ControlMove($g_hGui, "", $g_idMarker, 10 + $aRect[0], 40 - 3, $aRect[2] - $aRect[0] + 1, 3) ; 10 / 40 are LV coords EndFunc ;==>_MoveMarker ;======================================================================== Func _TrackPopupMenu($hMenu, $hWnd, $iX, $iY) ; $TPM_RETURNCMD (0x0100) returns the menu item identifier of the user's selection in the return value. Return DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0x0100, "int", $iX, "int", $iY, "hwnd", $hWnd, "ptr", 0)[0] EndFunc ;==>_TrackPopupMenu ;======================================================================== Func _GetText($iItem, $iSubItem) Local $sGetText If $g_iSortDir = 0x0400 Then ; $HDF_SORTUP = 0x0400 $sGetText = $g_aSubArray[$g_tIndex.arr($iItem + 1)][$iSubItem] Else $sGetText = $g_aSubArray[$g_tIndex.arr(($g_bShowAllRows ? $g_iRows : $g_iSearch) - $iItem)][$iSubItem] EndIf Return $sGetText EndFunc ;==>_GetText ;======================================================================== Func _ComputeLV_Height($iNb_Rows, $bHeader = True, $bBorder = True) Local $hGui = GUICreate("", 170, 200) ; test values not to be changed Local $idListView = GUICtrlCreateListView("Col 0", 10, 10, 150, 180) ; border $WS_EX_CLIENTEDGE Local $iClient_Height = WinGetClientSize(GUICtrlGetHandle($idListView))[1] Local $iBorder_Height = 180 - $iClient_Height Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0) Local $iHeader_Height = _WinAPI_GetWindowHeight($hHeader) Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($idListView, True) GUIDelete($hGui) Return ($iNb_Rows * $iGetItemSpacingY) + ($bHeader ? $iHeader_Height : 0) + ($bBorder ? $iBorder_Height : 0) EndFunc ;==>_ComputeLV_Height Concerning a new function _ComputeLV_Height() found at the very end of the script, it should deserve explanations in a separate post. Basically, the function computes an accurate ListView height, which will be used during Gui and LV creation. For example, in the script, we want 40 full rows in LV, without having any additional vertical pixels (half rows) at the bottom of the LV. If I'm not mistaken, this is how LarsJ's "788" listview height (40 full rows) is computed : LarsJ's 788 LV height = => 40 rows X 19 = 760 => Header height = 24 => 2 x 2 borders = 4 ($WS_EX_CLIENTEDGE) 760 + 24 + 4 = 788 When we run the script, each one of us will have a different ListView height because of our different OS's (or the modif. we did in Windows parameters). On my computer, it's not 788 but 624 which is returned by _ComputeLV_Height (to have 40 full rows). Here is a pic of my last 40th row, without a single additional or missing pixel (1st row is 0, which explains "39" written at the end of the last row). Let's hope the function will work for any user. By the way, I got rid of the Combo "All" option in the script, as it is already complicated enough. Thanks for not asking new implementations as I won't be here often from now on. Good luck to you all and stay healthy Edit #1 - update June 10th 2021 : disable 2 search buttons (Prev / Next) if number of matched rows = number of initial array rows, when search field is not empty (a . with RegEx search for instance that matches all rows) and "Show all rows" is ticked in context menu. Edited June 10, 2021 by pixelsearch Updates when needed (details above) Lion66, Musashi, argumentum and 1 other 4 Link to comment Share on other sites More sharing options...
Norm73 Posted June 10, 2021 Share Posted June 10, 2021 (edited) Hi pixelsearch Thank you for your efforts and explanations. Sorry if I was too impatient or persistent. I understand you well as I have had similar problems lately myself.☹️ I don't know if this would be appropriate, but I wish you a speedy recovery... P.S. I was not very attentive, but after looking at my code with a fresh head, I found a solution for my question. The solution turned out to be very simple. Added two new lines and that's it. Edited June 10, 2021 by Norm73 Link to comment Share on other sites More sharing options...
Lion66 Posted June 10, 2021 Share Posted June 10, 2021 Spoiler 50 minutes ago, Norm73 said: Hi pixelsearch Thank you for your efforts and explanations. Sorry if I was too impatient or persistent. I understand you well as I have had similar problems lately myself.☹️ I don't know if this would be appropriate, but I wish you a speedy recovery... I join in ... Link to comment Share on other sites More sharing options...
powerofos Posted June 10, 2021 Share Posted June 10, 2021 (edited) @pixelsearch Hi, thanks for your example! is $LVS_EX_HEADERDRAGDROP style acceptable? i found a problem about Zero column draged. column 0 rectangle is not correct after dragged to other column position, especially horizontal direction listview croll. both : _GUICtrlListView_GetSubItemRect and _GUICtrlListView_GetItemRect can't get the correct positon of column zero, any other idea about this question? Edited June 10, 2021 by powerofos Link to comment Share on other sites More sharing options...
pixelsearch Posted June 10, 2021 Share Posted June 10, 2021 (edited) 9 hours ago, Norm73 said: I don't know if this would be appropriate, but I wish you a speedy recovery... @Norm73 it's always appropriate when it comes directly from the heart. Thanks a lot Concerning your last script, I found this kind of issue (pic below). Here is a way to reproduct it in 5 steps : * Choose column 1 (combo) i.e Strings-2 got the orange marker above (searched column) * Edit search : type an a * Column string-2 : one left click to sort ascending * Combo : switch from Filter to Scroll * Column string-2 : one left click to sort descending. Display : Maybe the 2 new lines you added 1 hour ago fixed it ? Fingers crossed but I doubt it. I remember having had exactly the same issue sometimes, while scripting version "2s", before using the 2nd set of array $g_aSubArray2, plus the new array $aSearch. If you look at code in "2s" you'll find several additional tests when it comes to sorting or switching from "Filter" to "Scroll" as you name it, (which corresponds to my context menu option "Show all rows" when it's checked / unchecked). Also, I remember this kind of line that fixed the issue. The index sort function Func _UpdateIndex() needs to be updated. In the precedent versions (before "2s") it was : $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_iSearch) Now in "2s", it becomes : $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_bShowAllRows ? $g_iRows : $g_iSearch) This kind of ternary test in now found in plenty of lines in "2s", this could explain why you're having the issue. Good luck. 7 hours ago, powerofos said: is $LVS_EX_HEADERDRAGDROP style acceptable? @powerofos it's an interesting question but as the data source is an array, then maybe it could work if the array was rearranged internally after you drag a header to another place, not forgetting the sub-arrays and related arrays like these ones : Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60] Global $g_aColAligns[$g_iCols] = [2, 1, 1, 1, 1, 1] ; $HDF_LEFT = 0, $HDF_RIGHT = 1, $HDF_CENTER = 2 But I'm not even sure of what I just wrote, it requires tests. I remember having used $LVS_EX_HEADERDRAGDROP in my CSV file editor and it worked really fine (though it wasn't a virtual LV). I wish the solution is to keep the arrays untouched but I don't remember exactly how it worked there ! As you're using the script, may I suggest you download the "2s" version just above because it's the version that could eventually be updated. But please think twice before allowing the draggable header style : wouldn't it be better to create the LV with the headers correctly placed and keep them untouched ? In this link, LarsJ wrote : "A workaround for all these HDN_DIVIDERDBLCLICK issues is to set correct column widths from the start, and then disable column resize through dragging/double-clicking header dividers by adding the HDS_NOSIZING style to the header control" I don't know what LarsJ thinks about the $LVS_EX_HEADERDRAGDROP style when it comes to Virtual ListViews. Maybe he'll tell us. I'll try to test it when I got some time. Edited June 10, 2021 by pixelsearch typo Norm73 1 Link to comment Share on other sites More sharing options...
powerofos Posted June 10, 2021 Share Posted June 10, 2021 (edited) @pixelsearch @Larsj LarsJ 's solution in $NM_CUSTOMDRAW ; Stage 4 Case BitOR( $CDDS_ITEMPOSTPAINT, $CDDS_SUBITEM ) ; After a subitem has been painted If DllStructGetData( $tNMLVCustomDraw, "iSubItem" ) = $iSubItem Then Local $hDC = DllStructGetData( $tNMLVCUSTOMDRAW, "hdc" ) ; Device context If $iSubItem Then $aRect = _GUICtrlListView_GetSubItemRect( $hListView, $iItem, $iSubItem ); Subitem rectangle Else ; $iSubItem = 0, the listview item, first column ; This calculation of $aRect for the listview item is necessary to get ; things to work properly if $LVS_EX_HEADERDRAGDROP style is applied. $aRect = _GUICtrlListView_GetItemRect( $hListView, $iItem, 2 ) ; 2 - The bounding rectangle of the item text $aRect[0] -= 4 EndIf If $bNotXP And $iSubItem = 0 And $aColumnOrder[1] <> 0 Then DllStructSetData( $tRect, "Left", $aRect[0] ) ; Margin for first column if dragged to another position Else DllStructSetData( $tRect, "Left", $aRect[0]+4 ) EndIf DllStructSetData( $tRect, "Top", $aRect[1] ) DllStructSetData( $tRect, "Right", $aRect[2] ) DllStructSetData( $tRect, "Bottom", $aRect[3] ) _WinAPI_FillRect( $hDC, $tRect, $hBrush ) ; Fill subitem background _WinAPI_SetBkMode( $hDC, $TRANSPARENT ) ; Transparent background _WinAPI_SetTextColor( $hDC, 0x000000 ) ; Set black text color DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + 2 ) ; Adjust rectangle If $bNotXP Then DllStructSetData( $tRect, "Top", DllStructGetData( $tRect, "Top" ) + 2 ) _WinAPI_DrawText( $hDC, _GUICtrlListView_GetItemText( $hListView, $iItem, $iSubItem ), $tRect, $DT_WORD_ELLIPSIS ) ; Draw text EndIf Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors used _GUICtrlListView_GetItemRect to get rectangle position if column index is 0 i can't used it in Itemdraw message , it crash After several modifications, I use the position of a column to the left of column 0 to fix the position problem of column 0 Case $HDN_ENDDRAG GUICtrlSendToDummy($vColsDragDummy) ;----------------------------------------------- Case $vColsDragDummy $vColsOrder = _GUICtrlListView_GetColumnOrderArray($hVDListView) If $vColsOrder[1] <> 0 Then For $i = 1 To $vColsOrder[0] Step 1 If $vColsOrder[$i] = 0 Then $vColsZero_LeftID = $vColsOrder[$i-1] ExitLoop EndIf Next EndIf ;ItemDraw message:------------------------------------ Local $tSubRect = DllStructCreate("struct;long Left;long Top;long Right;long Bottom;endstruct");$tagRECT DllStructSetData($tSubRect,"Top",$iColsID) DllStructSetData($tSubRect,"Left",0);$LVIR_BOUNDS = 0 GUICtrlSendMsg($VDListView,0x1000+56,$iRowsID,DllStructGetPtr($tSubRect)) If $iColsID = 0 Then If $vColsOrder[1] <> 0 Then Local $tSub0Rect = DllStructCreate("struct;long Left;long Top;long Right;longBottom;endstruct");_GUICtrlListView_GetSubItemRect($hVDListView,$iRowsID,$vColsZero_LeftID) DllStructSetData($tSub0Rect,"Top",$vColsZero_LeftID) DllStructSetData($tSub0Rect,"Left",0);$LVIR_BOUNDS = 0 GUICtrlSendMsg($VDListView,0x1000+56,$iRowsID,DllStructGetPtr($tSub0Rect)) DllStructSetData($tSubRect,"Left",DllStructGetData($tSub0Rect,"Right")) EndIf DllStructSetData($tSubRect,"Right",DllStructGetData($tSubRect,"Left")+GUICtrlSendMsg($VDListView,0x1000+29,0,0));$LVM_GETCOLUMNWIDTH EndIf Any other good idea ? Edited June 11, 2021 by powerofos Link to comment Share on other sites More sharing options...
Norm73 Posted June 10, 2021 Share Posted June 10, 2021 (edited) 23 hours ago, pixelsearch said: Now in "2s", it becomes : $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_bShowAllRows ? $g_iRows : $g_iSearch) Thank you very much! I fixed everything. I thought you weren't going to release your latest version so I decided to do the best I can myself. Now I will deal with the latest version. And I'm looking forward to your CSV editor. I hope one day you will post it too. Edited June 11, 2021 by Norm73 Link to comment Share on other sites More sharing options...
ldub Posted June 11, 2021 Share Posted June 11, 2021 On 6/10/2021 at 3:44 PM, pixelsearch said: Now in "2s", it becomes : $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_bShowAllRows ? $g_iRows : $g_iSearch) Thank you very much! I fixed everything. @Norm73 Could you send the hole code, please !! Link to comment Share on other sites More sharing options...
ldub Posted June 12, 2021 Share Posted June 12, 2021 10 hours ago, ldub said: Thank you very much! I fixed everything. @Norm73 Could you send the all code, please !! Link to comment Share on other sites More sharing options...
Norm73 Posted June 12, 2021 Share Posted June 12, 2021 (edited) Hi ldub If no errors are found, this script will no longer be corrected. I think it's better to keep an eye on the latest versions from pixelsearch. VrtListView_Search.zip Edited June 19, 2021 by Norm73 Link to comment Share on other sites More sharing options...
pixelsearch Posted June 12, 2021 Share Posted June 12, 2021 (edited) @Norm73 glad you made it @powerofos I tried to script a version "2t" with draggable headers, but I'm not really convinced with it, we'll see... expandcollapse popup#include <ComboConstants.au3> #include <EditConstants.au3> #include <GuiListView.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include "RandomArray.au3" ; LarsJ #include "DrawItem.au3" ; " Opt("MustDeclareVars", 1) Global $g_iRows = 10000, $g_iCols = 6, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60] Global $g_aColAligns[$g_iCols] = [2, 1, 1, 1, 1, 1] ; $HDF_LEFT = 0, $HDF_RIGHT = 1, $HDF_CENTER = 2 Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows, $g_iLvItem = -1 Global $g_aArray, $g_aSubArray, $g_aSubArray2, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]") Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String" Global $g_idContextDummy, $g_hContextmenu, $g_iItem = -1, $g_iSubItem = -1, $g_bShowAllRows = False, $g_bRightClickHeaders = True Global $fListViewHasFocus = 1 ; trying this for now, we'll see... Global $g_bHeaderDragged = False, $g_aColumnOrder[$g_iCols + 1] ; format of this array in help file topic _GUICtrlListView_GetColumnOrderArray ; related to LV ex. style $LVS_EX_HEADERDRAGDROP and message $HDN_ENDDRAG Global $g_bPreventHeaderChanges = False ; important on older OS (allow header changes at some moments in case $HDS_NOSIZING is not recognized) ; it's crucial as style $LVS_EX_HEADERDRAGDROP & owner drawn are present in the script : no column resizing ! Example() Func Example() ; Generate array & one index _Generate_All($g_aArray) $g_aSubArray = $g_aArray $g_aSubArray2 = $g_aArray $g_tDefaultIndex = $g_tIndex ; Compute LV_Height based on 40 full visible rows, no additional vertical pixel. Local $iLV_Height = _ComputeLV_Height(40) ; only 1 param => LV got headers & border ConsoleWrite("Computed LV height = " & $iLV_Height & " pixels" & @crlf & @crlf) ; Create GUI $g_hGui = GUICreate("Virtual ListView (owner drawn) + Incr. Search + draggable Headers (2t)", 630 + 20, $iLV_Height + 30 + 20) ; Create Edit control (search) + dummy control Local $idEdit = GUICtrlCreateEdit("", 120, 10, 182, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL)) $g_hEdit = GUICtrlGetHandle($idEdit) $g_idEditDummy = GUICtrlCreateDummy() ; Create 2 Search Buttons (Prev / Next) & 2 Accelerator Keys (F3 / Shift+F3) Local $idPrev = GUICtrlCreateButton("Prev", 312, 10, 50, 20) Local $idNext = GUICtrlCreateButton("Next", 370, 10, 50, 20) GUICtrlSetState($idPrev, $GUI_DISABLE) GUICtrlSetState($idNext, $GUI_DISABLE) Local $aAccelKeys[2][2] = [ [ "{F3}", $idNext ], [ "+{F3}", $idPrev ] ] GUISetAccelerators($aAccelKeys) ; Create ComboBox control (how to search : RegEx or Normal ?) Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable) GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow) ; Create ComboBox control (column where to search) + dummy control GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER)) $g_idComboCol = GUICtrlCreateCombo("0", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST)) $g_iSearchCol = 0 ; default column where to search (changeable) Local $iSearchColPrev = $g_iSearchCol For $i = 1 To $g_iCols - 1 GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol) Next $g_idComboColDummy = GUICtrlCreateDummy() ; Create Label control (number of matching results) Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN)) ; Create ListView $g_idListView = GUICtrlCreateListView("", 10, 40, 630, $iLV_Height, BitOr($GUI_SS_DEFAULT_LISTVIEW, $LVS_OWNERDATA, $LVS_OWNERDRAWFIXED), $WS_EX_CLIENTEDGE) _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOr($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_HEADERDRAGDROP)) $g_hListView = GUICtrlGetHandle($g_idListView) $g_hHeader = _GUICtrlListView_GetHeader($g_idListView) For $i = 0 To $g_iCols - 1 _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i]) _GUICtrlHeader_SetItemFormat($g_hHeader, $i, $HDF_STRING + $g_aColAligns[$i]) $g_aColumnOrder[$i + 1] = $i Next $g_aColumnOrder[0] = $g_iCols Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($g_idListView, True) ; No ListView column resizing by dragging header dividers (LarsJ) . (Personal : it's nearly mandatory when $LVS_EX_HEADERDRAGDROP + owner drawn) ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _ DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING ) ; Create Marker (an orange line placed above the header of the column being searched) $g_idMarker = GUICtrlCreateLabel("", 0, 0) GUICtrlSetBkColor(-1, 0xFFA060) ; orange _MoveMarker($g_iSearchCol) _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol) ; Create Context menu $g_idContextDummy = GUICtrlCreateDummy() Local $idContextMenu = GUICtrlCreateContextMenu($g_idContextDummy) Local $idContext_GetText = GUICtrlCreateMenuItem("Get text", $idContextMenu) GUICtrlCreateMenuItem("", $idContextMenu) Local $idContext_ShowAll = GUICtrlCreateMenuItem("Show all rows", $idContextMenu) Local $idContext_RightClickHeaders = GUICtrlCreateMenuItem("Right-click Headers", $idContextMenu) GUICtrlSetState(-1, 1) ; $GUI_CHECKED = 1 $g_hContextMenu = GuiCtrlGetHandle($idContextMenu) ; Sorting information $g_iSortDir = 0x0400 ; $HDF_SORTUP = 0x0400 Local $iSortCol = -1, $iSortColPrev = -1 ; Register message handlers GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ; Sets the virtual number of items in a virtual list-view control GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0) GUISetState(@SW_SHOW) ; Message loop $g_bPreventHeaderChanges = True ; prevent changes in headers (no resizing) until next _GUICtrlHeader_SetItemFormat instruction. Local $aSearch[$g_iRows], $iStart While 1 Switch GUIGetMsg() Case $g_idComboCol, $g_idComboColDummy, $idSearchHow $g_iSearchCol = GUICtrlRead($g_idComboCol) $sSearchHow = GUICtrlRead($idSearchHow) Select Case $g_iSearchCol <> $iSearchColPrev _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase $iSearchColPrev = $g_iSearchCol Case $sSearchHow <> $sSearchHowPrev $sSearchHowPrev = $sSearchHow Case Else ; no change in both Combo controls (same search column, same search way) ContinueLoop EndSelect ContinueCase Case $g_idEditDummy $g_bPreventHeaderChanges = False ; surround next instruction that changes a header (allow it, then prevent it just after) _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + ($iSortCol > -1 ? $g_aColAligns[$iSortCol] : 0)) $g_bPreventHeaderChanges = True ; no more changes until next _GUICtrlHeader_SetItemFormat instruction. $g_sSearch = GUICtrlRead($idEdit) $g_tIndex = $g_tDefaultIndex ; Sorting information (same as 1st launch, to avoid descending displays when all unsorted rows are shown) $g_iSortDir = 0x0400 ; $HDF_SORTUP = 0x0400 $iSortCol = -1 $iSortColPrev = -1 If $g_sSearch = "" Then ; Empty search string, display all rows $g_aSubArray = $g_aArray $g_aSubArray2 = $g_aArray $g_iSearch = $g_iRows GUICtrlSetState($idPrev, $GUI_DISABLE) GUICtrlSetState($idNext, $GUI_DISABLE) Else ; Find rows matching the search string $g_iSearch = 0 If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1") EndIf ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf) Dim $aSearch[$g_iRows] For $i = 0 To $g_iRows - 1 If StringRegExp($g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch) Then For $j = 0 To $g_iCols - 1 $g_aSubArray2[$g_iSearch][$j] = $g_aArray[$i][$j] Next $aSearch[$i] = 1 ; include row ($aSearch will serve if user wants to show all rows and click Next / Prec) $g_iSearch += 1 EndIf Next ; Delete eventual temporary subindexes For $i = 0 To $g_iCols - 1 If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String" Next $g_aSubArray = $g_bShowAllRows ? $g_aArray : $g_aSubArray2 GUICtrlSetState($idPrev, ($g_bShowAllRows And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSetState($idNext, ($g_bShowAllRows And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) EndIf GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows")) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT) Case $g_idListView ; Sort $iSortCol = GUICtrlGetState($g_idListView) If $iSortCol <> $iSortColPrev Then $g_bPreventHeaderChanges = False _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING + ($iSortColPrev > -1 ? $g_aColAligns[$iSortColPrev] : 0)) $g_bPreventHeaderChanges = True EndIf ; Set $g_tIndex + eventual update of $g_aIndex[$iSortCol] OR $g_aIndexTemp[$iSortCol] If $g_bShowAllRows Or $g_sSearch = "" Or $g_iSearch = $g_iRows Then _UpdateIndex($g_aIndex, $iSortCol) Else _UpdateIndex($g_aIndexTemp, $iSortCol) EndIf $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP)) $g_bPreventHeaderChanges = False _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_aColAligns[$iSortCol] + $g_iSortDir) $g_bPreventHeaderChanges = True GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) $iSortColPrev = $iSortCol ; Update $aSearch (eventually) + automatic display of 1st match (eventually too) If $g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows Then _Update_aSearch($aSearch, $iSortCol, $idPrev, $idNext) EndIf Case $idNext $iStart = $g_iLvItem = - 1 ? 0 : $g_iLvItem < $g_iRows - 1 ? $g_iLvItem + 1 : 0 While 1 ; The while loop is used to start all over when last row in the listview is reached For $i = $iStart To $g_iRows - 1 If $aSearch[$i] And $i > $g_iLvItem Then ExitLoop Next If $i < $g_iRows Then ExitLoop $g_iLvItem = -1 $iStart = 0 Wend If $g_iLvItem > - 1 Then _GUICtrlListView_SetItemSelected($g_idListView, $g_iLvItem, False, False) _GUICtrlListView_ClickItem($g_idListView, $i) If $iStart = 0 Then Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) _GUICtrlListView_Scroll($g_idListView, 0, ($i - $iGetTopIndex) * $iGetItemSpacingY) EndIf _GUICtrlListView_SetItemSelected($g_idListView, $i, True, True) Case $idPrev $iStart = $g_iLvItem = - 1 ? $g_iRows - 1 : $g_iLvItem > 0 ? $g_iLvItem - 1 : $g_iRows - 1 While 1 ; The while loop is used to start all over when first row in the listview is reached For $i = $iStart To 0 Step -1 If $aSearch[$i] And $i < $g_iLvItem Then ExitLoop Next If $i > -1 Then ExitLoop $g_iLvItem = $g_iRows $iStart = $g_iRows - 1 Wend If $g_iLvItem > - 1 Then _GUICtrlListView_SetItemSelected($g_idListView, $g_iLvItem, False, False) _GUICtrlListView_ClickItem($g_idListView, $i) If $iStart = 0 Then Local $iGetTopIndex = _GUICtrlListView_GetTopIndex($g_idListView) _GUICtrlListView_Scroll($g_idListView, 0, ($i - $iGetTopIndex) * $iGetItemSpacingY) EndIf _GUICtrlListView_SetItemSelected($g_idListView, $i, True, True) Case $g_idContextDummy If $g_iItem > - 1 Then _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSubItem) GUICtrlSetState($idContext_GetText, $g_iItem = - 1 ? $GUI_DISABLE : $GUI_ENABLE) Local $iMenu_Choice = _TrackPopupMenu($g_hContextMenu, $g_hGui, MouseGetPos(0), MouseGetPos(1)) If $iMenu_Choice Then Switch $iMenu_Choice Case $idContext_GetText ; Get text Local $sGetText = _GetText($g_iItem, $g_iSubItem) MsgBox($MB_TOPMOST, "Context menu", "Row = " & $g_iItem & " Column = " & $g_iSubItem & " Text = " & $sGetText & @lf) Case $idContext_ShowAll ; Show all rows ? If BitAND(GUICtrlRead($idContext_ShowAll), 4) = 4 Then ; $GUI_UNCHECKED = 4 GUICtrlSetState($idContext_ShowAll, 1) ; $GUI_CHECKED = 1 $g_bShowAllRows = True $g_aSubArray = $g_aArray ; Set $g_tIndex + eventual update $g_aIndex[$iSortCol] If $iSortCol > - 1 Then _UpdateIndex($g_aIndex, $iSortCol) ; Update $aSearch (eventually) + automatic display of 1st match (eventually too) If $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows Then _Update_aSearch($aSearch, $iSortCol, $idPrev, $idNext) Else GUICtrlSetState($idContext_ShowAll, 4) ; $GUI_UNCHECKED = 4 $g_bShowAllRows = False $g_aSubArray = $g_aSubArray2 ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] If $iSortCol > - 1 Then _UpdateIndex($g_aIndexTemp, $iSortCol) ; No eventual update of $aSearch because $g_bShowAllRows = False (2 buttons Prev & Next are disable in this case) EndIf GUICtrlSetState($idPrev, ($g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSetState($idNext, ($g_bShowAllRows And $g_sSearch And $g_iSearch And $g_iSearch < $g_iRows) ? $GUI_ENABLE : $GUI_DISABLE) GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_bShowAllRows ? $g_iRows : $g_iSearch, 0) Case $idContext_RightClickHeaders ; Activate headers right-click ? If BitAND(GUICtrlRead($idContext_RightClickHeaders), 4) = 4 Then ; $GUI_UNCHECKED = 4 GUICtrlSetState($idContext_RightClickHeaders, 1) ; $GUI_CHECKED = 1 $g_bRightClickHeaders = True Else GUICtrlSetState($idContext_RightClickHeaders, 4) ; $GUI_UNCHECKED = 4 $g_bRightClickHeaders = False EndIf EndSwitch Endif Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?) _MoveMarker($g_iSearchCol) Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd ; Cleanup GUIDelete($g_hGui) EndFunc ;==>Example ;======================================================================== Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Display items in an owner drawn ListView (called once per item : no separate messages for each subitem +++) Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE ) Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0xFFFF00 ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0x00FFFF ) ; Yellow and cyan, BGR Local Static $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE ) Local $tDrawItem = DllStructCreate( $tagDRAWITEM, $lParam ), $itemID = DllStructGetData( $tDrawItem, "itemID" ), $iState = DllStructGetData( $tDrawItem, "itemState" ), $hDC = DllStructGetData( $tDrawItem, "hDC" ), $sItemText, $iSubItem If $g_bHeaderDragged Then $g_bHeaderDragged = False $g_aColumnOrder = _GUICtrlListView_GetColumnOrderArray($g_idListView) ; function inside WM_DRAWITEM (or too late to catch new order in time) For $iIdxHeader = 1 To $g_aColumnOrder[0] If $g_aColumnOrder[$iIdxHeader] = 0 Then ExitLoop ; subitem 0 always found (even if LV got 1 column only) Next Local $iSwapVal = $g_aColumnOrder[1] $g_aColumnOrder[1] = 0 ; eventual dragged subitem 0 index always in 1st position in loop to come => selected row background will be ok $g_aColumnOrder[$iIdxHeader] = $iSwapVal _MoveMarker(GUICtrlRead($g_idComboCol)) EndIf ; Loop through columns ($i is the column index) ; As $LVS_EX_HEADERDRAGDROP extended style is in the script, replace here all $i with $g_aColumnOrder[$i + 1] , i.e $iSubItem For $i = 0 To $g_iCols - 1 $iSubItem = $g_aColumnOrder[$i + 1] ; 1st one will be 0 (by design) to get a correct selected row background (columns not being all left aligned) ; Subitem rectangle DllStructSetData( $tRect, 2, $iSubItem ) ; Top DllStructSetData( $tRect, 1, $LVIR_BOUNDS ) ; Left GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $itemID, $pRect ) ; DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 0 ) ; Left margin ; If first subitem, the rectangle has been calculated for the entire listview item ($LVM_GETSUBITEMRECT) ; Compensate for this by setting the width of the rectangle to the width of the first column ($LVM_GETITEMRECT message and not $LVM_GETCOLUMNWIDTH ?) ; Before that, if item is selected, fill the entire listview item width with highlight background color (it will be too late if not done now) If $iSubItem = 0 Then If BitAND( $iState, $ODS_SELECTED ) Then DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $fListViewHasFocus = 1 ? $hBrushHighLight : $hBrushButtonFace ) ; _WinAPI_FillRect Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, 0) DllStructSetData( $tRect, 1, $aRect[0]) ; left DllStructSetData( $tRect, 3, $aRect[2]) ; right EndIf ; Retrieve subitem text If $g_iSortDir = 0x0400 Then ; $HDF_SORTUP = 0x0400 $sItemText = $g_aSubArray[$g_tIndex.arr($itemID + 1)][$iSubItem] Else $sItemText = $g_aSubArray[$g_tIndex.arr(($g_bShowAllRows ? $g_iRows : $g_iSearch) - $itemID)][$iSubItem] EndIf ; Subitem rectangle for right and center aligned columns If $g_aColAligns[$iSubItem] Then ; $HDF_LEFT = 0, $HDF_RIGHT = 1, $HDF_CENTER = 2 DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 Switch $g_aColAligns[$iSubItem] Case 1 ; $HDF_RIGHT DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 ) Case 2 ; $HDF_CENTER DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + ( DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tRect, "Left" ) - DllStructGetData( $tSize, "X" ) ) / 2 - 3 ) EndSwitch EndIf ; Subitem text color DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", BitAND( $iState, $ODS_SELECTED ) ? $fListViewHasFocus = 1 ? 0xFFFFFF : 0x000000 : 0x000000 ) ; _WinAPI_SetTextColor ; Draw subitem text DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText ; $iSubItem is column index, $g_iSearchCol is the search column ; Mark matching substring only if column index = search column If $iSubItem <> $g_iSearchCol Then ContinueLoop ; Matching substring? If $g_sSearch Then Local $sMatch = StringRegExp( $sItemText, "(?i)" & $g_sSearch, 1 ) If Not @error Then ; match found Local $extended = @extended, $iLen = StringLen( $sMatch[0] ) ; Rectangle for matching substring DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32 DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) ) ; Fill rectangle with yellow or cyan (selected) background color DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 1 ) ; Top margin DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", BitAND( $iState, $ODS_SELECTED ) ? $hBrushCyan : $hBrushYellow ) ; _WinAPI_FillRect ; Draw matching substring in rectangle DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) - 1 ) ; Top margin DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText EndIf EndIf Next Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc ;======================================================================== Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam) Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam) Switch HWnd(DllStructGetData($tNMHDR, "hWndFrom")) Case $g_hListView Switch DllStructGetData($tNMHDR, "Code") Case $LVN_ITEMCHANGED Local $tNMListView = DllStructCreate($tagNMLISTVIEW, $lParam) Local $iNewState = DllStructGetData($tNMListView, "NewState") If BitAND($iNewState, $LVIS_FOCUSED) Then $g_iLvItem = DllStructGetData($tNMListView, "Item") Case $NM_RCLICK Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $lParam) $g_iItem = DllStructGetData($tInfo, "Index") ;~ If $g_iItem > -1 Then ; valid row (no, accept also non-valid row for several reasons and check $g_iItem in context menu code) $g_iSubItem = DllStructGetData($tInfo, "SubItem") GUICtrlSendToDummy($g_idContextDummy) ;~ EndIf EndSwitch Case $g_hHeader Switch DllStructGetData($tNMHDR, "Code") Case $NM_RCLICK If $g_bRightClickHeaders Then Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView) ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header) If $aHit[1] > - 1 Then ; valid column GUICtrlSetData($g_idComboCol, $aHit[1]) GUICtrlSendToDummy($g_idComboColDummy) EndIf EndIf Case $HDN_ENDDRAG ; related to LV extended style $LVS_EX_HEADERDRAGDROP $g_bHeaderDragged = True Return False ; allow the control to automatically place and reorder the item Case $HDN_BEGINTRACKW ; keep it here (older OS) Return True ; prevent tracking (especially when $LVS_EX_HEADERDRAGDROP + owner drawn) Case $HDN_ITEMCHANGINGW ; keep it here (older OS) . It takes care of $HDN_DIVIDERDBLCLICKW which is hard to prevent on older OS Return $g_bPreventHeaderChanges ? True : False ; True to prevent changes, False to allow them. EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY ;======================================================================== Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) Local $hWndFrom = $lParam Local $iCode = BitShift($wParam, 16) ; High word Switch $hWndFrom Case $g_hEdit Switch $iCode Case $EN_CHANGE GUICtrlSendToDummy($g_idEditDummy) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND ;======================================================================== Func _Generate_All(ByRef $g_aArray) ConsoleWrite("$g_iRows = " & $g_iRows & " $g_iCols = " & $g_iCols & @CRLF) $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz") ; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters For $i = 0 To $g_iRows - 1 $g_tIndex.arr($i + 1) = $i Next EndFunc ;==>_Generate_All ;======================================================================== Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows) Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]") Local $pIndex = DllStructGetPtr($tIndex) Local Static $hDll = DllOpen("kernel32.dll") Local Static $hDllComp = DllOpen("shlwapi.dll") Local $lo, $hi, $mi, $r ; Sorting by one column For $i = 1 To $iRows - 1 $lo = 0 $hi = $i - 1 Do $mi = Int(($lo + $hi) / 2) $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0] Switch $r Case -1 $hi = $mi - 1 Case 1 $lo = $mi + 1 Case 0 ExitLoop EndSwitch Until $lo > $hi DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4) DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1)) Next Return $tIndex EndFunc ;==>_SortArrayStruct ;======================================================================== Func _UpdateIndex(ByRef $aIndex, $iCol) If VarGetType($aIndex[$iCol]) = "DLLStruct" Then $g_tIndex = $aIndex[$iCol] Else $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_bShowAllRows ? $g_iRows : $g_iSearch) $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++) EndIf EndFunc ;==>_UpdateIndex ;======================================================================== Func _Update_aSearch(ByRef $aSearch, $iSortCol, $idPrev, $idNext) $g_iSearch = 0 Dim $aSearch[$g_iRows] For $i = 0 To $g_iRows - 1 If StringRegExp($g_aArray[$g_tIndex.arr($g_iSortDir = 0x0400 ? $i + 1 : $g_iRows - $i)][$g_iSearchCol], "(?i)" & $g_sSearch) Then $aSearch[$i] = 1 ; include row $g_iSearch += 1 EndIf Next If $g_iSearch And $iSortCol = $g_iSearchCol Then GUICtrlSetState($idPrev, $GUI_ENABLE) GUICtrlSetState($idNext, $GUI_ENABLE) $g_iLvItem = - 1 ; comment this line if 1st match not to be displayed on 1st row ControlClick($g_hGui, "", $idNext) EndIf EndFunc ;==>_Update_aSearch ;======================================================================== Func _MoveMarker($iCol) Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ControlMove($g_hGui, "", $g_idMarker, 10 + $aRect[0], 40 - 3, $aRect[2] - $aRect[0] + 1, 3) ; 10 / 40 are LV coords EndFunc ;==>_MoveMarker ;======================================================================== Func _TrackPopupMenu($hMenu, $hWnd, $iX, $iY) ; $TPM_RETURNCMD (0x0100) returns the menu item identifier of the user's selection in the return value. Return DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0x0100, "int", $iX, "int", $iY, "hwnd", $hWnd, "ptr", 0)[0] EndFunc ;==>_TrackPopupMenu ;======================================================================== Func _GetText($iItem, $iSubItem) Local $sGetText If $g_iSortDir = 0x0400 Then ; $HDF_SORTUP = 0x0400 $sGetText = $g_aSubArray[$g_tIndex.arr($iItem + 1)][$iSubItem] Else $sGetText = $g_aSubArray[$g_tIndex.arr(($g_bShowAllRows ? $g_iRows : $g_iSearch) - $iItem)][$iSubItem] EndIf Return $sGetText EndFunc ;==>_GetText ;======================================================================== Func _ComputeLV_Height($iNb_Rows, $bHeader = True, $bBorder = True) Local $hGui = GUICreate("", 170, 200) ; test values not to be changed Local $idListView = GUICtrlCreateListView("Col 0", 10, 10, 150, 180) ; border $WS_EX_CLIENTEDGE Local $iClient_Height = WinGetClientSize(GUICtrlGetHandle($idListView))[1] Local $iBorder_Height = 180 - $iClient_Height Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0) Local $iHeader_Height = _WinAPI_GetWindowHeight($hHeader) Local $iGetItemSpacingY = _GUICtrlListView_GetItemSpacingY($idListView, True) GUIDelete($hGui) Return ($iNb_Rows * $iGetItemSpacingY) + ($bHeader ? $iHeader_Height : 0) + ($bBorder ? $iBorder_Height : 0) EndFunc ;==>_ComputeLV_Height Edited June 13, 2021 by pixelsearch replaced 3 lines 346 to 348, fingers crossed. Norm73 1 Link to comment Share on other sites More sharing options...
powerofos Posted June 12, 2021 Share Posted June 12, 2021 (edited) @pixelsearch Thanks a lot. As i reported before, it crash. try run it and scroll by mouse, and you will see...... Edited June 12, 2021 by powerofos Link to comment Share on other sites More sharing options...
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