Norm73 Posted June 26, 2021 Posted June 26, 2021 (edited) Thanks @pixelsearchfor the quick response I checked it a few more times. All three options work very well. It's hard to say which is better in terms of functionality, but the second variant (with +6 Pxl) looks really better. Edited June 26, 2021 by Norm73 pixelsearch 1
LarsJ Posted July 4, 2021 Author Posted July 4, 2021 (edited) Multi column searchIn this example a ListView Combo control is used to define search filters. The main ListView is a custom drawn and virtual ListView. Searching and sorting is based on array indexes (1d arrays of integers). ListView ComboIn the ListView Combo control in the image, a search is defined for rows that matches search filters in 3 columns: 2 x's in the Strings column, dates in the range 2015 - 2019 in the Dates column, and 2 consecutive 5 digits in the Floats column. Note that the rows in the ListView Combo corresponds to the columns in $g_aArray and in the main ListView. There are 5 columns in the ListView Combo: Col is the column index in $g_aArray/main ListView The Checkbox is used to enable/disable a search filter Column is the column name in the main ListView Ord is the order of the search filter. The order is filled in automatically when a search filter is enabled and a search string is entered. The order is cleared on a disabled search filter or an empty search string. RegEx indicates RegEx or Normal search Click the Checkbox to switch The Search string column contains search strings that are entered through an Edit control. Click to open the Edit control. Move the mouse out of the Edit control to close it. Note the order of the 3 search filters in the image. The Strings column with order 0 is the first and most significant search filter. The Dates column with order 1 is the next search filter. The Floats column with order 2 is the last and least significant search filter. Rule: For search filters provided with an order, you can only change the least significant search filter (with the highest order). If you want to change the order of the Strings and Floats search filters so that Floats gets order 0 and Strings order 2, you must first disable all 3 search filters in this order: Floats, Dates, Strings. Click the Checkbox in first column to disable the search filters. Then you have to enable the 3 search filters again in this order: Floats, Dates, Strings. Performance considerations are the reason for this rule. Because the search is an incremental search, it's expected to be very dynamic and responsive. There must be no delays if you e.g. adds or removes a character in a search string. Therefore, it's important that only the least significant search filter can be changed. If only the least significant search filter can be changed, only one search and one sort must be recalculated. If it was possible to update the search string in the most significant search filter then both the search and the sort would have to be recalculated for all search filters. In this example with 6 columns in $g_aArray and main ListView, 6 search filters are possible. If all 6 search filters had to be recalculated for both the search and the sort, then it would certainly cause a significant delay. Use the Reset button to completely reset all search filters. Main ListViewThe main ListView uses two different WM_NOTIFY message handlers: WM_NOTIFY_All is used to display all rows in $g_aArray when no search filter is applied. WM_NOTIFY_Search is used to display the matching rows in $g_aArray when one or more search filters are applied. LVN_GETDISPINFO code in WM_NOTIFY_All: Case $LVN_GETDISPINFOW Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam ) Local Static $tText = DllStructCreate( "wchar Text[50]" ), $pText = DllStructGetPtr( $tText ) $tText.Text = $g_aArray[$g_aIndex[($g_iSortDir=$HDF_SORTUP?$tNMLVDISPINFO.Item:$g_iRows-1-$tNMLVDISPINFO.Item)]][$tNMLVDISPINFO.SubItem] $tNMLVDISPINFO.Text = $pText Return Note usage of the sort index $g_aIndex: $g_aArray[$g_aIndex[($g_iSortDir=$HDF_SORTUP?$tNMLVDISPINFO.Item:$g_iRows-1-$tNMLVDISPINFO.Item)]] LVN_GETDISPINFO code in WM_NOTIFY_Search: Case $LVN_GETDISPINFOW Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam ) If Not ( $g_aSearchCols[$tNMLVDISPINFO.SubItem] == "" ) Then Return ; Skip $g_aSearchCols Local Static $tText = DllStructCreate( "wchar Text[50]" ), $pText = DllStructGetPtr( $tText ) $tText.Text = $g_aArray[$g_aSearchRows[$g_aSearchIndex[($g_iSortDir=$HDF_SORTUP?$tNMLVDISPINFO.Item:$g_iSearchRows-1-$tNMLVDISPINFO.Item)]]][$tNMLVDISPINFO.SubItem] $tNMLVDISPINFO.Text = $pText Return Note the If statement that skips columns, which are subsequently drawn with custom draw code. Usage of the search index $g_aSearchRows (see below) and the sort index $g_aSearchIndex (see below): $g_aArray[$g_aSearchRows[$g_aSearchIndex[($g_iSortDir=$HDF_SORTUP?$tNMLVDISPINFO.Item:$g_iSearchRows-1-$tNMLVDISPINFO.Item)]]] The custom draw code in WM_NOTIFY_Search that shows the part of a subitem text that exactly matches the search string on a cyan or yellow background is similar to the custom draw code here. Main LoopThe interesting messages in the main loop regarding multi column search are $g_idComboListViewSubItem0,3,4, $idExtractRowsBySearchFilter and $idListViewSort. $g_idComboListViewSubItem0,3,4 handles changes to a search filter made through the ListView Combo control in columns 0, 3, and 4. Rows that match the search filter are extracted from $g_aArray through a $idExtractRowsBySearchFilter message. Finally, the rows are sorted through a $idListViewSort message. SearchingCode to extract the rows that match a search filter is implemented this way for a RegEx search: For $i = 0 To $iSearchRowsAll - 1 If StringRegExp( $g_aArray[$aSearchRowsAll[$i]][$g_iCLVRow], $sSearch ) Then $g_aSearchRows[$g_iSearchRows] = $aSearchRowsAll[$i] $g_iSearchRows += 1 EndIf Next For the first search filter with order 0 in the image $iSearchRowsAll = $g_iRows. And $aSearchRowsAll = $g_aIndex as calculated for the last column (the current sort column) in the main ListView. $g_iSearchRows is the number of matching rows and $g_aSearchRows is an index of the matching rows. For the last search filter with order 2 in the image $iSearchRowsAll = $g_iSearchRows as calculated for the previous search filter with order 1. And $aSearchRowsAll = $g_aSearchIndex (see below) also as calculated for the previous search filter with order 1. SortingSorting the rows that match a search filter is performed this way: $g_tIndex = FAS_Sort2DArrayAu3( $g_aArray, $aCompare, $g_aSearchRows, $g_iSearchRows ) AccessVariables01( IntStructToArraySearchMtd, $g_aSearchIndex ) $g_aSearchRows and $g_iSearchRows are passed to FAS_Sort2DArrayAu3() as parameters. FAS_Sort2DArrayAu3() returns a sort index as a DllStruct, which is converted to a 1d array of integers in $g_aSearchIndex. $g_aSearchIndex is the sort index for rows extracted through a search filter. FAS_Sort2DArrayIndexFunc() performs the sorting when the two additional parameters are passed to FAS_Sort2DArrayAu3(). This is the first part of FAS_Sort2DArrayIndexFunc() to sort strings: Func FAS_Sort2DArrayIndexFunc( $aArray, $aCompare, $aSearchRows, $iSearchRows ) Local $iRows = $iSearchRows, $tIndex = DllStructCreate( "uint[" & $iRows & "]" ), $pIndex = DllStructGetPtr( $tIndex ) Static $hDll = DllOpen( "kernel32.dll" ) Local $c, $a, $lo, $hi, $mi If $aCompare[0][1] Then ; Sorting by one column of strings $c = $aCompare[0][0] $a = $aCompare[0][2] For $i = 1 To $iRows - 1 $lo = 0 $hi = $i - 1 Do $mi = Int( ( $lo + $hi ) / 2 ) Switch StringCompare( $aArray[$aSearchRows[$i]][$c], $aArray[$aSearchRows[DllStructGetData($tIndex,1,$mi+1)]][$c] ) * $a 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 The very last code box in the Main ListView section above shows how to extract a row from $g_aArray, taking into account both $g_aSearchRows and $g_aSearchIndex. Miscellaneous On 6/10/2021 at 12:43 AM, pixelsearch said: 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) That's not correct. Starting with Windows 7, all the different parts that make up a listview item or subitem (checkbox, icon, subicon, label, sublabel, left and top margins) have the same size. Only older Windows versions use other sizes. Only very few Windows styles will change these sizes on Windows 7 and later. CodeMultiColumnSearch.7z I'll probably add a few more posts in relation to these examples. Then I'll include all the examples in the 7z-file at bottom of first post. Edited July 4, 2021 by LarsJ Gianni, powerofos, Norm73 and 1 other 2 2 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
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