Announcement

Collapse
No announcement yet.

How to enter incremental search mode.

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to enter incremental search mode.

    I want to implement a feature that allows the user to select items in a listview by typing the first (and perhaps subsequent) character, like file explorer.

    I have WM_Char by subclassing. I found LVM_FINDITEM which looked interesting and LVM_GETISEARCHSTRING which I'm guessing you pass result of getisearchstring to finditme via a LV_FINDINFO structure. What I don't get is the line in the definition of getisearchstring -

    Returns the number of characters in the incremental search string, or zero if the list-view control is not in incremental search mode.
    How do I get into incremental search mode?

    Edit. I've found an old Dominic Mitchell example which does exactly that. Need to try to inderstand how it works.

    Later edit. Dominic's code does the job by looping thro the LV manually comparing which is OK but I'm still curious. Also found some code by Richard Angell which uses LVM_FINDITEM but does not appearently use incremental search mode.
    Last edited by James McNab; 25 Jul 2019, 05:52 PM.

  • #2
    So it seems that LVM_GETISEARCHSTRING and incremental search mode will remain a mystery, almost nothing on google. Guess I'll learn to live with it.

    Comment


    • #3
      James,

      Did you find / try the code in this thread?
      Rgds, Dave

      Comment


      • #4
        Did you find / try the code in this thread?
        Thanks Dave, no I didn't. I seem to have a problem with the search feature on this board. When ever I remember I do a google site specific search but forgot this time.

        I ended up using Dominic's code slightly tweaked so that the search was case insensitive and the found item was moved as near to the top as possible. I will delve into the mysteries of LVM_GETISEARCHSTRING some other time.

        Code:
         
        FUNCTION Form1_AutoSelect _ ( _ BYVAL hWnd AS DWORD, _ ' window handle szItem AS ASCIIZ _ ' text to match ) AS LONG LOCAL lItemIdx AS LONG ' zero-based index of listview item LOCAL szText AS ASCIIZ * %MAX_PATH ' text buffer LOCAL tlv_item AS LV_ITEM ' structure that receives info about item LOCAL lCount AS LONG ' number of items in listview control LOCAL cblText AS LONG ' number of characters in listview item text LOCAL CountPerPage AS LONG LOCAL HiddenTopRow AS LONG LOCAL HiddenBotRow AS LONG LOCAL PositionInLV AS LONG LOCAL trc AS RECT tlv_item.pszText = VARPTR(szText) ' Initialize the address buffer that receives item text tlv_item.cchTextMax = %MAX_PATH ' tlv_item.iSubItem = %LABELCOL lItemIdx = 0 lCount = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMCOUNT, 0, 0) ' get num of items in LV DO IF lItemIdx >= lCount THEN EXIT DO ' quit if beyond last item cblText = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMTEXT, lItemIdx, BYVAL VARPTR(tlv_item)) ' read first char of label IF ISTRUE cblText THEN ' szText = LEFT$(szText, LEN(szItem)) ' Math on length and characters IF ISFALSE lstrcmpi(szItem, szText) THEN ' case inspecific compare tlv_item.state = %LVIS_FOCUSED OR %LVIS_SELECTED ' match found between typed and item label first char tlv_item.stateMask = %LVIS_FOCUSED OR %LVIS_SELECTED SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SETITEMSTATE, lItemIdx, BYVAL VARPTR(tlv_item)) ' select found item SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_ENSUREVISIBLE, lItemIdx, %FALSE) ' ensure item visible CountPerPage = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETCOUNTPERPAGE, 0, 0) ' number of items visible HiddenTopRow = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETTOPINDEX, 0, 0) ' num items hidden above displayed LV HiddenBotRow = lCount - (HiddenTopRow + CountPerPage) ' num items hidden below displayed LV PositionInLV = lItemIdx - HiddenTopRow ' position of found item SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMRECT, 0, BYVAL VARPTR(trc)) ' get item height in pixels ' Note! even in report mode vert LV scroll is specified in pixels IF PositionInLV > HiddenBotRow THEN ' scroll as near to top as possible SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SCROLL, 0, HiddenBotRow * (trc.nBottom - trc.nTop)) ELSE SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SCROLL, 0, PositionInLV * (trc.nBottom - trc.nTop)) END IF EXIT FUNCTION END IF END IF INCR lItemIdx LOOP END FUNCTION
        Sorry for the double spaced thing and the weird indenting, it looked ok as posted, couldn't figure out how to stop that.

        Comment


        • #5
          Hi James,

          Re LVM_GETISEARCHSTRING.

          Listview controls have a built-in incremental search.

          Looks like LVM_GETSEARCHSTRING can be used to give a window into it's behaviour.

          From what I can figure, the search only works on the first column and only accepts letters (not numbers).

          Below is a small test to investigate how it performs.

          While the LV has focus, type a Letter (Not Number!!) to start a search from the current selection.
          If a match is found the row is shifted into view. If further keys, pressed within a short time period, refine the search, the next matching cadidate will be selected and shifted into view.
          Code:
          #Compile Exe
          #Dim All
          #Resource Manifest, 1, "C:\PBWin10\samples\DDT\Address\XPTheme.xml"   ' Themed (CommCtrl v6)
          #Include Once "WIN32API.INC"
          
          %IDD_DIALOG1 =  101
          %LV_MAIN     = 1001
          %IDC_BUTTON1 = 1002
          
          Global gLvOldProc As Dword
          '------------------/
          
          Function PBMain()
           Local hDlg  As Dword
            Dialog New 0, "Dialog1", 183, 105, 201, 121, %WS_Caption Or %WS_SysMenu To hDlg
              Control Add ListView, hDlg, %LV_MAIN, "Listview1", 5, 5, 190, 85, %WS_TabStop Or %LVS_Report
              Control Add Button,   hDlg, %IDC_BUTTON1, "Button1", 85, 95, 50, 15
          
              SampleListView hDlg, %LV_MAIN, 5, 7
              gLvOldProc = SetWindowLongW(GetDlgItem(hDlg, %LV_MAIN), %GWL_WNDPROC, ByVal CodePtr(LvSubClassProc))
            Dialog Show Modal hDlg, Call DlgProc
          
          End Function
          '------------------/PBMain
          
          Function LVSubClassProc(ByVal hWnd&, ByVal wMsg&, ByVal wParam&, ByVal lParam&) As Long
           Local SearchString As Asciiz * %Max_Path
           Local iCount       As Long
            Select Case wMsg&
              Case %WM_KeyUp
                iCount& = SendMessage(hWnd&, %LVM_GetISearchString, 0&, ByVal VarPtr(SearchString))
                Dialog Set Text GetParent(hWnd&), Using$(" #  & ", iCount&, SearchString)
          
              Case %WM_Destroy
                ' Restore the listview controls original callback procedure
                SetWindowLongW(hWnd&, %GWL_WNDPROC, gLvOldProc)
                Exit Function
            End Select
            Function = CallWindowProc(gLvOldProc, hWnd&, wMsg&, wParam&, lParam&)
          End Function
          '------------------/LvSubClassProc
          
          CallBack Function DlgProc()
            Select Case As Long Cb.Msg
              Case %WM_Command
                Select Case Cb.Ctl
                  Case %IdCancel : Dialog End Cb.Hndl, 0    ' Esc
                End Select
            End Select
          End Function
          '------------------/DlgProc
          
          Function SampleListView(ByVal hDlg As Dword, ByVal lID As Long, ByVal lColCnt As Long, ByVal _
            lRowCnt As Long) As Long
            Local lCol   As Long
            Local lRow   As Long
            Local lStyle As Long
          
            ListView Get StyleXX hDlg, lID To lStyle
            ListView Set StyleXX hDlg, lID, lStyle Or %LVS_Ex_FullRowSelect Or %LVS_Ex_GridLines
          
            ' Load column headers.
            For lCol = 1 To lColCnt
              ListView Insert Column hDlg, lID, lCol, Using$(" Column # ", lCol), 0, 0
            Next lCol
          
            ' Load sample data.
           '  For lRow = 1 To lRowCnt
               ListView Insert Item hDlg, lID, 1, 0, Using$("Cola # R #", 1, 1)
               ListView Insert Item hDlg, lID, 2, 0, Using$("Colb # R #", 1, 2)
               ListView Insert Item hDlg, lID, 3, 0, Using$("Colc # R #", 1, 3)
               ListView Insert Item hDlg, lID, 4, 0, Using$("Cold # R #", 1, 4)
               ListView Insert Item hDlg, lID, 5, 0, Using$("Cole # R #", 1, 5)
               ListView Insert Item hDlg, lID, 6, 0, Using$("Colf # R #", 1, 5)
               ListView Insert Item hDlg, lID, 7, 0, Using$("Colg # R #", 1, 5)
          '   Next lRow
          
            For lRow = 1 To lRowCnt : For lCol = 2 To lColCnt
              ListView Set Text hDlg, lID, lRow, lCol, Using$("C # R #", lCol, lRow)
            Next lRow : Next lCol
          
            ' Auto size columns.
            For lCol = 1 To lColCnt
              ListView Fit Header hDlg, lID, lCol
            Next lCol
          End Function
          '------------------/SampleListView
          Rgds, Dave

          Comment


          • #6
            Thanks a million Dave, I'm trying to use column 2 and I can't change because I have a checkbox in column 1 but info is good to know because it does some clever tricks. Pity about the column 1 thing.

            Comment


            • #7
              Oh dear, I appear to be having a senior moment. Dave I looked at your code and realized I don't understand at all. It displays the desired behavior even with LVM_GETISEARCHSTRING remmed and I can't see where you compare keyboard input with LV text. Please put an old man out of his misery and explainas you would to a small child or a golder retreiver.

              Comment


              • #8
                From what I could see, LVM_GETISEARCHSTRING doesn't cause the behaviour, rather, incremental searching is a built-in feature of a Listview control that has input focus.

                What that message does is ask the Listview to expose the search string as the user types it.

                The control goes about the business of Finding and Selecting the next candidate that matches the search string. If it is not in view, the built-in behaviour takes care of Scrolling found item into view.

                The searching is done on the first column, below the current selection, rolling over to the top row as required.

                There is a time delay / pause detection that is sensitive to the speed of the user's typing.
                By default the control will reject numeric input and gives a messagebox-like beep when the input is not allowed or if the search string goes out of sync with any candidates.

                It looks as if the first column can include check boxes and text, in which case the built in search does operate.
                Code:
                    Control Add ListView, hDlg, %LV_MAIN, "Listview1", 5, 5, 190, 85, %WS_TabStop Or %LVS_Report
                    ListView Set StyleXX hDlg, %LV_MAIN, %LVS_Ex_CheckBoxes Or %LVS_Ex_GridLines Or %LVS_Ex_FullRowSelect
                Rgds, Dave

                Comment


                • #9
                  Thanks Dave, I had no idea a LV could do that.

                  Edit. I can confirm Dave's supposition that incremental search is builtin to an LV in report mode and it only works on column 0. Although I can add checkboxes to column 0 (although I'm not sure how to access them) and is still works unfortunatly my app has a non-eye candy graphic between the checkbox and the label which prevents that.

                  What I could do is create a zero width column 0 and copy the labels I'm trying to incrementaly search (if I can get checkboxes in col 1 to work)
                  Last edited by James McNab; 28 Jul 2019, 11:56 AM.

                  Comment


                  • #10
                    Hi James,

                    Here is a more elaborate version of the test code which investigates using CheckBoxes and Images in the first column, while still responding to incremental searches..
                    Code:
                    #Compile Exe
                    #Dim All
                    #Resource Manifest, 1, "C:\PBWin10\samples\DDT\Address\XPTheme.xml"   ' opt Themed (CommCtrl v6)
                    #Include Once "Win32api.inc"
                    
                    #Resource Icon, Face1, "C:\PBWin10\samples\SDK\Tray\Face1.ico"
                    #Resource Icon, Face2, "C:\PBWin10\samples\SDK\Tray\Face2.ico"
                    
                    %LV_MAIN     = 1001
                    %IDC_BUTTON1 = 1002
                    %IDC_Sbar    = 1003
                    
                    Global gLvOldProc As Dword
                    '------------------/
                    
                    Function PBMain()
                     Local hDlg  As Dword
                      Dialog New 0, "Dialog1", 100, 100, 280, 140, %WS_Caption Or %WS_SysMenu To hDlg
                        Control Add ListView, hDlg, %LV_MAIN, "Listview1", 5, 5, 270, 85, %WS_TabStop Or %LVS_Report Or %LVS_SHOWSELALWAYS
                        Control Add Button,   hDlg, %IDC_BUTTON1, "GetState", 85, 95, 50, 15
                        Control Add Statusbar, hDlg, %IDC_Sbar, "", 0, 0, 0, 0
                    
                        SampleListView hDlg, %LV_MAIN, 5, 7
                        gLvOldProc = SetWindowLongW(GetDlgItem(hDlg, %LV_MAIN), %GWL_WNDPROC, ByVal CodePtr(LvSubClassProc))
                      Dialog Show Modal hDlg, Call DlgProc
                    
                    End Function
                    '------------------/PBMain
                    
                    Function LVSubClassProc(ByVal hWnd&, ByVal wMsg&, ByVal wParam&, ByVal lParam&) As Long
                     Local SearchString As AsciiZ * %Max_Path
                     Local iCount       As Long
                      Select Case wMsg&
                        Case %WM_KeyUp
                          iCount& = SendMessage(hWnd&, %LVM_GetISearchString, 0&, ByVal VarPtr(SearchString))
                          Dialog Set Text GetParent(hWnd&), Using$(" #  & ", iCount&, SearchString)
                    
                        Case %WM_Destroy
                          ' Restore the listview controls original callback procedure
                          SetWindowLongW(hWnd&, %GWL_WNDPROC, gLvOldProc)
                          Exit Function
                      End Select
                      Function = CallWindowProc(gLvOldProc, hWnd&, wMsg&, wParam&, lParam&)
                    End Function
                    '------------------/LvSubClassProc
                    
                    CallBack Function DlgProc()
                      Select Case As Long Cb.Msg
                        Case %WM_Command
                          Select Case Cb.Ctl
                            Case %IdCancel : Dialog End Cb.Hndl, 0    ' Esc
                            Case %IDC_BUTTON1                         ' Get checkbox state
                              If CB.CtlMsg = %BN_Clicked Or CB.CtlMsg = 1 Then
                                Local CurSel, ItemState As Long
                                  ListView Get Select Cb.Hndl, %LV_MAIN To CurSel
                                  Control Send Cb.Hndl, %LV_MAIN, %LVM_GetItemState, CurSel-1, _
                                  %LVIS_Focused Or %LVIS_Selected Or %LVIS_StateImageMask To ItemState
                                  StatusBar Set Text Cb.Hndl, %IDC_Sbar, 1, 0, "Item" + Str$(CurSel) + "  " + Bin$(ItemState, 16)
                                  Control Set Focus Cb.Hndl, %LV_MAIN
                              End If
                          End Select
                    
                        Case %WM_SysCommand
                          If (CbWParam And &HFFF0) = %SC_Close Then
                            Dialog End Cb.Hndl, 0
                          End If
                    
                        Case %WM_Notify
                          Select Case Cb.NmId
                            Case %LV_MAIN
                              Select Case Cb.NmCode
                                Case %LVN_ItemChanged
                                 Local pNMLV As NMLISTVIEW Ptr
                                  pNMLV = Cb.LParam
                                  If (@pNMLV.uChanged And %LVIF_STATE) = %LVIF_STATE Then  ' if state has changed, you can
                                    '  use the LVIS_STATEIMAGEMASK mask to isolate the one-based index of the state image (check mark).
                                    Local iRes As Long
                                    iRes = @pNMLV.uNewState And %LVIS_STATEIMAGEMASK
                                    If iRes Then
                                      StatusBar Set Text Cb.Hndl, %IDC_Sbar, 1, 0, "Item"+Str$(@pNMLV.iItem+1)+" "+ _
                                      Switch$(iRes = &H01000, "UnChecked", iRes = &H02000, "Checked") + _
                                      "  " + Bin$(@pNMLV.uNewState, 16) +" (State bits) " + TIME$
                                      ListView Set Image Cb.Hndl, %LV_MAIN, @pNMLV.iItem+1, IIF(iRes = &H02000, 2, 1)
                                    End If
                                  End If
                              End Select
                          End Select
                      End Select
                    End Function
                    '------------------/DlgProc
                    
                    Function SampleListView(ByVal hDlg As Dword, ByVal lID As Long, ByVal lColCnt As Long, ByVal _
                                            lRowCnt As Long) As Long
                     Local lStyle As Long
                      ListView Get StyleXX hDlg, lID To lStyle
                      ListView Set StyleXX hDlg, lID, lStyle Or %LVS_Ex_CheckBoxes Or %LVS_Ex_FullRowSelect Or %LVS_Ex_GridLines
                    
                      ' Load column headers.
                     Local lCol, lRow As Long
                      For lCol = 1 To lColCnt
                        ListView Insert Column hDlg, lID, lCol, Using$("  Column #  ", lCol), 0, 2
                      Next lCol
                    
                     Local hImgList As Dword
                        ImageList New Icon 16,16,24,2 To hImgList
                        ImageList Add Icon hImgList, "Face1"
                        ImageList Add Icon hImgList, "Face2"
                    
                    
                      ' Load sample data.
                        ListView Insert Item hDlg, lID, 1, 0, Using$("Cola # R # ", 1, 1)
                        ListView Insert Item hDlg, lID, 2, 0, Using$("Colb # R # ", 1, 2)
                        ListView Insert Item hDlg, lID, 3, 0, Using$("Colc # R # ", 1, 3)
                        ListView Insert Item hDlg, lID, 4, 0, Using$("Cold # R # ", 1, 4)
                        ListView Insert Item hDlg, lID, 5, 0, Using$("Cole # R # ", 1, 5)
                        ListView Insert Item hDlg, lID, 6, 0, Using$("Colf # R # ", 1, 5)
                        ListView Insert Item hDlg, lID, 7, 0, Using$("Colg # R # ", 1, 5)
                    
                      For lRow = 1 To lRowCnt : For lCol = 2 To lColCnt
                        ListView Set Text hDlg, lID, lRow, lCol, Using$("C # R #", lCol, lRow)
                      Next lRow : Next lCol
                    
                      ListView Set ImageList hDlg, lID, hImgList, %lvsil_Small
                      For lRow = 1 To lRowCnt
                       ListView Set Image hDlg, lID, lRow, 1
                      Next lRowCnt
                    
                      ' Auto size columns.
                      For lCol = 1 To lColCnt
                        ListView Fit Header hDlg, lID, lCol
                      Next lCol
                    End Function
                    '------------------/SampleListView
                    Rgds, Dave

                    Comment


                    • #11
                      Oooh thanks Dave, just saw your latest code, thanks a million.

                      Comment


                      • #12
                        For the record here's what I ended up with.

                        Although as Dave pointed out, it is possible to have a checkbox, a graphic and an item all in column 0 (Item in col 0 is the requirment for IS appearently) it gets kindda clunky without putting leading spaces on the Items, which borks IS.

                        Based on Dominic's code I simulated the IS logic.

                        Code:
                        '-------------------------------------------------------------------------------
                        '
                        ' PROCEDURE: SnapShotMain_ListView1_OnChar
                        ' PURPOSE:   ListView1 WM_CHAR message handler.
                        ' RETURN:    TRUE if message was processed, FALSE if it was not.
                        '
                        '-------------------------------------------------------------------------------
                        
                        FUNCTION SnapShotMain_ListView1_OnChar _
                          ( _
                          BYVAL hWnd      AS DWORD, _ ' control handle
                          BYVAL lCharCode AS LONG, _  ' character code
                          BYVAL lKeyData  AS LONG _   ' key data
                          ) AS LONG
                        
                          LOCAL szItem AS ASCIIZ * 2
                        
                          szItem = CHR$(lCharCode)
                          Form1_AutoSelect(hWnd, szItem)
                          PreviousChar = szItem
                          FUNCTION = %TRUE
                        END FUNCTION
                        
                        
                        '-------------------------------------------------------------------------------
                        '
                        ' PROCEDURE: Form1_AutoSelect
                        ' PURPOSE:  Selects an item in the listview control if it matches the
                        '            given text.
                        '
                        '-------------------------------------------------------------------------------
                        
                        FUNCTION Form1_AutoSelect _
                          ( _
                          BYVAL hWnd    AS DWORD, _ ' window handle
                                szItem  AS ASCIIZ _ ' text to match
                          ) AS LONG
                        
                          LOCAL lItemIdx       AS LONG                ' zero-based index of listview item
                          LOCAL szText         AS ASCIIZ * %MAX_PATH  ' text buffer
                          LOCAL tlv_item       AS LV_ITEM             ' structure that receives info about item
                          LOCAL lCount         AS LONG                ' number of items in listview control
                          LOCAL cblText        AS LONG                ' number of characters in listview item text
                          LOCAL CountPerPage   AS LONG
                          LOCAL HiddenTopRow   AS LONG
                          LOCAL HiddenBotRow   AS LONG
                          LOCAL PositionInLV   AS LONG
                          LOCAL trc            AS RECT
                          LOCAL SelectedLine   AS LONG
                        
                          tlv_item.pszText    = VARPTR(szText)                              ' Initialize the address buffer that receives item text
                          tlv_item.cchTextMax = %MAX_PATH                                   '
                          tlv_item.iSubItem = %LABELCOL
                        
                          IF szItem = PreviousChar THEN                                                                               ' check for repeat char
                            lItemIdx = SendMessage(ghWnd_SnapShotMain_ListView1,%LVM_GETNEXTITEM, -1, %LVNI_SELECTED)                 ' get currently selected item
                            lItemIdx = lItemIdx + 1                                                                                   ' start at item after currently selected
                          ELSE
                            lItemIdx = 0                                                                                              ' start at first item (index 0)
                          END IF
                          lCount  = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMCOUNT, 0, 0)                                ' get num of items in LV
                        
                          DO
                            IF lItemIdx >= lCount THEN EXIT DO                                                                        ' quit if beyond last item
                            cblText = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMTEXT, lItemIdx, BYVAL VARPTR(tlv_item))   ' read first char of label
                            IF ISTRUE cblText THEN                                                                                    '
                              szText = LEFT$(szText, LEN(szItem))                                                                     ' Math on length and characters
                              IF ISFALSE lstrcmpi(szItem, szText) THEN                                                                ' case inspecific compare
                                tlv_item.state    = %LVIS_FOCUSED OR %LVIS_SELECTED                             ' match found between typed and item label first char
                                tlv_item.stateMask = %LVIS_FOCUSED OR %LVIS_SELECTED
                                SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SETITEMSTATE, lItemIdx, BYVAL VARPTR(tlv_item))        ' select found item
                                SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_ENSUREVISIBLE, lItemIdx, %FALSE)                       ' ensure item visible
                                CountPerPage = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETCOUNTPERPAGE, 0, 0)                  ' number of items visible
                                HiddenTopRow = SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETTOPINDEX, 0, 0)                      ' num items hidden above displayed LV
                                HiddenBotRow = lCount - (HiddenTopRow + CountPerPage)                                                 ' num items hidden below displayed LV
                                PositionInLV = lItemIdx - HiddenTopRow                                                                ' position of found item
                                SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_GETITEMRECT, 0, BYVAL VARPTR(trc))                     ' get item height in pixels
                                ' Note! even in report mode vert LV scroll is specified in pixels
                                IF PositionInLV > HiddenBotRow THEN                                                                   ' scroll as near to top as possible
                                  SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SCROLL, 0, HiddenBotRow * (trc.nBottom - trc.nTop))
                                ELSE
                                  SendMessage(ghWnd_SnapShotMain_ListView1, %LVM_SCROLL, 0, PositionInLV * (trc.nBottom - trc.nTop))
                                END IF
                                EXIT FUNCTION
                              END IF
                            END IF
                            INCR lItemIdx
                          LOOP
                        END FUNCTION
                        PreviousChar is a global and is i

                        Comment

                        Working...
                        X