Что нового

Элементы GUI Как объединить сортировку и поиск в виртуальном ListView

liond66

Новичок
Сообщения
102
Репутация
1
Здравствуйте.
Есть два скрипта (прикреплены):
LV-Search.au3 включает поиск по первому столбику в ListView.
LV-Sort.au3 включает сортировку столбиков при нажатии на заголовок в ListView.
Я пытаюсь объединить их в один скрипт, но это не совсем работает.
Проблема скорее всего в строках 174, 176.
Пожалуйста помогите исправить.

Код:
#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include <GuiHeader.au3>
#include <MsgBoxConstants.au3>
#include "Cursor.au3"

Opt("MustDeclareVars", 1)

Global $hGui, $hEdit, $idEditSearch, $hLV, $iItems = 5000, $aItems[$iItems][3], $aSearch[$iItems], $iSearch = 0, $sText

Global Const $hKernel32Dll = DllOpen("kernel32.dll")
Global $iRows
; Sorting
Global $iSortCol = -1, $iSortDir = 0, $tIndex

Example()

Func Example()

    ; Create GUI
    $hGui = GUICreate("Gui", 600, 230)

    ; Create Edit control
    Local $idEdit = GUICtrlCreateEdit("", 10, 10, 300 - 20, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL))
    $hEdit = GUICtrlGetHandle($idEdit)
    $idEditSearch = GUICtrlCreateDummy()

    ; Create ListView                                                Virtual listview
    Local $idLV = GUICtrlCreateListView("", 10, 40, 600 - 20, 200 - 20, $LVS_OWNERDATA, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES))
    $hLV = GUICtrlGetHandle($idLV)
    _GUICtrlListView_AddColumn($hLV, "Item", 130)
    _GUICtrlListView_AddColumn($hLV, "Sub1", 125)
    _GUICtrlListView_AddColumn($hLV, "Sub2", 130)

    Local $hHeader = _GUICtrlListView_GetHeader($hLV)
    Local $iSortColPrev = -1, $iSortArrow

    ; Handle $WM_COMMAND messages from Edit control
    ; To be able to read the search string dynamically while it's typed in
    GUIRegisterMsg($WM_COMMAND, "WM_COMMAND_Search")

    ; Handle $WM_NOTIFY messages from ListView
    ; Necessary to display the rows in a virtual ListView
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    ; Show GUI
    GUISetState(@SW_SHOW)


;;;;;;;;;; added for sort ;;;;;;;;;;;;;

    $iRows = 0
    Local $tStrings, $pStrings

    ; Fill array
    For $i = 1 To 1000
        $aItems[$i - 1][1] = "AAA" & $i
        $aItems[$i - 1][2] = "BBB" & $i + 2
    Next
    $iItems = $i
    $iRows = FillArray($aItems)

    $tStrings = DllStructCreate("uint[" & $iRows & "]")
    $pStrings = DllStructGetPtr($tStrings)

    SortStrings($aItems, $iRows, 0, $pStrings, $tStrings)
    SortStrings($aItems, $iRows, 1, $pStrings, $tStrings)
    SortStrings($aItems, $iRows, 2, $pStrings, $tStrings)

    GUICtrlSendMsg($idLV, $LVM_SETITEMCOUNT, $iRows, 0)

;;;;;;;;;;;;;;;;; end added for sort ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ; Set search array to include all items
    For $i = 0 To $iItems - 1
        $aSearch[$i] = $i
    Next
    $iSearch = $iItems

    ; Display items
    GUICtrlSendMsg($idLV, $LVM_SETITEMCOUNT, $iSearch, 0)

    ; Message loop
    While 1
        Switch GUIGetMsg()

            Case $idEditSearch
                Local $sSearch = GUICtrlRead($idEdit)
                If $sSearch = "" Then
                    ; Empty search string, display all rows
                    For $i = 0 To $iItems - 1
                        $aSearch[$i] = $i
                    Next
                    $iSearch = $iItems
                Else
                    ; Find rows matching the search string
                    $iSearch = 0
                    For $i = 0 To $iItems - 1
                        If StringInStr($aItems[$i][0], $sSearch) Then ; Normal search
                            ;If StringRegExp( $aItems[$i][0], $sSearch ) Then ; Reg. exp. search
                            $aSearch[$iSearch] = $i
                            $iSearch += 1
                        EndIf
                    Next
                EndIf
                ; Display items
                GUICtrlSendMsg($idLV, $LVM_SETITEMCOUNT, $iSearch, 0)
                ;ConsoleWrite(StringFormat("%4d", $iSearch) & " rows matching """ & $sSearch & """" & @CRLF)

            Case $idLV
                $iSortCol = GUICtrlGetState($idLV)
                $tIndex = $tStrings

                If $iSortColPrev > -1 Then _
                    _GUICtrlHeader_SetItemFormat($hHeader, $iSortColPrev, $HDF_STRING)
                If $iSortColPrev = $iSortCol Then
                    $iSortDir = 1 - $iSortDir
                    $iSortArrow = $iSortDir ? $HDF_SORTUP : $HDF_SORTDOWN
                Else
                    $iSortDir = 1
                    $iSortColPrev = $iSortCol
                    $iSortArrow = $HDF_SORTUP
                EndIf

                _GUICtrlHeader_SetItemFormat($hHeader, $iSortCol, $HDF_STRING + $iSortArrow)
                GUICtrlSendMsg($idLV, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0)
                DllCall("user32.dll", "int", "InvalidateRect", "hwnd", $hLV, "int", 0, "int", 1)

            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    DllClose($hKernel32Dll)
    GUIDelete()
EndFunc   ;==>Example

Func WM_COMMAND_Search($hWnd, $iMsg, $wParam, $lParam)
    Local $hWndFrom = $lParam
    Local $iCode = BitShift($wParam, 16) ; High word
    Switch $hWndFrom
        Case $hEdit
            Switch $iCode
                Case $EN_CHANGE
                    GUICtrlSendToDummy($idEditSearch)
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND_Search

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local Static $tText = DllStructCreate("wchar[50]")
    Local Static $pText = DllStructGetPtr($tText)

    Local $tNMHDR, $hWndFrom, $iCode
    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hLV
            Switch $iCode
                Case $LVN_GETDISPINFOW
                    Local $tNMLVDISPINFO = DllStructCreate($tagNMLVDISPINFO, $lParam)
                    If BitAND(DllStructGetData($tNMLVDISPINFO, "Mask"), $LVIF_TEXT) Then

                        Local $sItem = $aItems[$aSearch[DllStructGetData($tNMLVDISPINFO, "Item")]][DllStructGetData($tNMLVDISPINFO, "SubItem")]

                        If $iSortCol = -1 Then
                            Local $sItem = $aItems[$aSearch[DllStructGetData($tNMLVDISPINFO, "Item")]][DllStructGetData($tNMLVDISPINFO, "SubItem")]
                        Else
                            If $iSortDir Then
                                Local $sItem = $aItems[$aSearch[DllStructGetData($tIndex, 1, DllStructGetData($tNMLVDISPINFO, "Item") + 1)]][DllStructGetData($tNMLVDISPINFO, "SubItem")]
                            Else
                                Local $sItem = $aItems[$aSearch[DllStructGetData($tIndex, 1, $iRows - DllStructGetData($tNMLVDISPINFO, "Item"))]][DllStructGetData($tNMLVDISPINFO, "SubItem")]
                            EndIf
                        EndIf

                        DllStructSetData($tText, 1, $sItem)
                        DllStructSetData($tNMLVDISPINFO, "Text", $pText)
                        DllStructSetData($tNMLVDISPINFO, "TextMax", StringLen($sItem))


                    EndIf
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Func FillArray(ByRef $aItems)
    Local $aLetters[26] = ['A', 'B', 'C', 'D', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', _
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'], $s
    For $i = 0 To $iItems - 1
        $s = $aLetters[Random(0, 25, 1)]
        For $j = 1 To Random(10, 30, 1)
            $s &= $aLetters[Random(0, 25, 1)]
        Next
        $aItems[$i][0] = $s
    Next
EndFunc   ;==>FillArray

Func SortStrings(ByRef $aItems, $iRows, $iCol, $pIndex, $tIndex)
    Local $k, $s, $lo, $hi, $mi, $gt
    HourglassCursor(True)
    WinSetTitle($hGui, "", "Virtual ListViews. Sort strings: 0 rows")
    For $i = 0 To $iRows / 10000 - 1
        $k = $i * 10000
        For $j = 0 To 9999
            $s = $aItems[$k + $j][$iCol]
            ; Binary search
            $lo = 0
            $hi = $k + $j - 1
            While $lo <= $hi
                $mi = Int(($lo + $hi) / 2)
                If StringCompare($s, $aItems[DllStructGetData($tIndex, 1, $mi + 1)][$iCol]) < 0 Then
                    $gt = 0
                    $hi = $mi - 1
                Else
                    $gt = 1
                    $lo = $mi + 1
                EndIf
            WEnd
            ; Make space for the new value
            DllCall($hKernel32Dll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($k + $j - $mi) * 4)
            ; Insert new value
            DllStructSetData($tIndex, 1, $k + $j, $mi + 1 + $gt)
        Next
        WinSetTitle($hGui, "", "Virtual ListViews. Sort strings: " & $k + 10000 & " rows")
    Next
    HourglassCursor(False)
    WinSetTitle($hGui, "", "Virtual ListViews (sorted)")
EndFunc   ;==>SortStrings

Func SortNumbers(ByRef $aItems, $iRows, $iCol, $pIndex, $tIndex)
    Local $k, $n, $lo, $hi, $mi, $gt
    HourglassCursor(True)
    WinSetTitle($hGui, "", "Virtual ListViews. Sort numbers: 0 rows")
    For $i = 0 To $iRows / 10000 - 1
        $k = $i * 10000
        For $j = 0 To 9999
            $n = $aItems[$k + $j][$iCol]
            ; Binary search
            $lo = 0
            $hi = $k + $j - 1
            While $lo <= $hi
                $mi = Int(($lo + $hi) / 2)
                If $n < $aItems[DllStructGetData($tIndex, 1, $mi + 1)][$iCol] Then
                    $gt = 0
                    $hi = $mi - 1
                Else
                    $gt = 1
                    $lo = $mi + 1
                EndIf
            WEnd
            ; Make space for the new value
            DllCall($hKernel32Dll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($k + $j - $mi) * 4)
            ; Insert new value
            DllStructSetData($tIndex, 1, $k + $j, $mi + 1 + $gt)
        Next
        WinSetTitle($hGui, "", "Virtual ListViews. Sort numbers: " & $k + 10000 & " rows")
    Next
    HourglassCursor(False)
    WinSetTitle($hGui, "", "Virtual ListViews (sorted)")
EndFunc   ;==>SortNumbers

Сообщение автоматически объединено:


Сообщение автоматически объединено:

Файл Cursor.au3 используется в скрипте LV-Sort.au3
Сообщение автоматически объединено:

Почистил немного код.
Исправил сортировку и для простоты оставил только по возростанию.
Но по прежнему работают поиск и сортировка по отдельности.
Или строка 180, или строка 182.
Теперь вопрос сводится к тому, можно ли объединить в функции WM_NOTIFY два правила с одинаковым Case $LVN_GETDISPINFOW ?

Код:
#include <GUIConstants.au3>
#include <GuiListView.au3>

Opt("MustDeclareVars", 1)

Global $hGui, $hEdit, $idEditSearch, $hLV, $iItems = 1000, $aItems[$iItems][3], $aSearch[$iItems], $iSearch = 0, $iRows = $iItems
Global Const $hKernel32Dll = DllOpen("kernel32.dll")
Global $iSortCol = -1, $tIndex

Example()

Func Example()

    ; Create GUI
    $hGui = GUICreate("Gui", 600, 230)

    ; Create Edit control
    Local $idEdit = GUICtrlCreateEdit("", 10, 10, 300 - 20, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL))
    $hEdit = GUICtrlGetHandle($idEdit)
    $idEditSearch = GUICtrlCreateDummy()

    ; Create ListView                                                  Virtual listview
    Local $idLV = GUICtrlCreateListView("", 10, 40, 600 - 20, 200 - 20, $LVS_OWNERDATA, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES))
    $hLV = GUICtrlGetHandle($idLV)
    _GUICtrlListView_AddColumn($hLV, "Item", 220)
    _GUICtrlListView_AddColumn($hLV, "Sub1", 125)
    _GUICtrlListView_AddColumn($hLV, "Sub2", 130)

    Local $hHeader = _GUICtrlListView_GetHeader($hLV)
    Local $iSortColPrev = -1, $iSortArrow

    ; Handle $WM_COMMAND messages from Edit control
    ; To be able to read the search string dynamically while it's typed in
    GUIRegisterMsg($WM_COMMAND, "WM_COMMAND_Search")

    ; Handle $WM_NOTIFY messages from ListView
    ; Necessary to display the rows in a virtual ListView
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    GUISetState(@SW_SHOW)

    FillArray($aItems)

    Local $tStrings1 = DllStructCreate("uint[" & $iRows & "]"), $pStrings1 = DllStructGetPtr($tStrings1)
    Local $tStrings2 = DllStructCreate("uint[" & $iRows & "]"), $pStrings2 = DllStructGetPtr($tStrings2)
    Local $tStrings3 = DllStructCreate("uint[" & $iRows & "]"), $pStrings3 = DllStructGetPtr($tStrings3)
    SortStrings($aItems, $iRows, 0, $pStrings1, $tStrings1)
    SortStrings($aItems, $iRows, 1, $pStrings2, $tStrings2)
    SortStrings($aItems, $iRows, 2, $pStrings3, $tStrings3)
    $tIndex = $tStrings1

    ; Set search array to include all items
    For $i = 0 To $iItems - 1
        $aSearch[$i] = $i
    Next
    $iSearch = $iItems

    ; Display items
    GUICtrlSendMsg($idLV, $LVM_SETITEMCOUNT, $iSearch, 0)

    While 1
        Switch GUIGetMsg()

            Case $idEditSearch
                Local $sSearch = GUICtrlRead($idEdit)
                If $sSearch = "" Then
                    ; Empty search string, display all rows
                    For $i = 0 To $iItems - 1
                        $aSearch[$i] = $i
                    Next
                    $iSearch = $iItems
                Else
                    ; Find rows matching the search string
                    $iSearch = 0
                    For $i = 0 To $iItems - 1
                        If StringInStr($aItems[$i][0], $sSearch) Then ; Normal search
                            $aSearch[$iSearch] = $i
                            $iSearch += 1
                        EndIf
                    Next
                EndIf
                ; Display items
                GUICtrlSendMsg($idLV, $LVM_SETITEMCOUNT, $iSearch, 0)

            Case $idLV
                $iSortCol = GUICtrlGetState($idLV)
                Switch $iSortCol
                    Case 0
                        $tIndex = $tStrings1
                    Case 1
                        $tIndex = $tStrings2
                    Case 2
                        $tIndex = $tStrings3
                EndSwitch

                _GUICtrlHeader_SetItemFormat($hHeader, $iSortColPrev, $HDF_STRING)
                _GUICtrlHeader_SetItemFormat($hHeader, $iSortCol, $HDF_STRING + $HDF_SORTUP)
                GUICtrlSendMsg($idLV, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0)
                DllCall("user32.dll", "int", "InvalidateRect", "hwnd", $hLV, "int", 0, "int", 1)
                $iSortColPrev = $iSortCol


            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    DllClose($hKernel32Dll)
    GUIDelete()
EndFunc   ;==>Example

Func FillArray(ByRef $aItems)
    Local $aLetters[26] = ['A', 'B', 'C', 'D', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', _
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'], $s
    For $i = 0 To $iItems - 1
        $s = $aLetters[Random(0, 25, 1)]
        For $j = 1 To Random(10, 30, 1)
            $s &= $aLetters[Random(0, 25, 1)]
        Next
        $aItems[$i][0] = $s
        $aItems[$i][1] = "AAA" & $i
        $aItems[$i][2] = "BBB" & $i + 2
    Next
EndFunc   ;==>FillArray

Func SortStrings(ByRef $aItems, $iRows, $iCol, $pIndex, $tIndex)
    Local $s, $lo, $hi, $mi
    For $j = 0 To $iRows - 1
        $s = $aItems[$j][$iCol]
        ; Binary search
        $lo = 0
        $hi = $j - 1
        While $lo <= $hi
            $mi = Int(($lo + $hi) / 2)
            If StringCompare($s, $aItems[DllStructGetData($tIndex, 1, $mi + 1)][$iCol]) < 0 Then
                $hi = $mi - 1
            Else
                $lo = $mi + 1
            EndIf
        WEnd
        ; Make space for the new value
        If $j > $mi Then _
                DllCall($hKernel32Dll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($j - $mi) * 4)
        ; Insert new value
        DllStructSetData($tIndex, 1, $j, $mi + 1 + ($lo = $mi + 1))
    Next
EndFunc   ;==>SortStrings

Func WM_COMMAND_Search($hWnd, $iMsg, $wParam, $lParam)
    Local $hWndFrom = $lParam
    Local $iCode = BitShift($wParam, 16) ; High word
    Switch $hWndFrom
        Case $hEdit
            Switch $iCode
                Case $EN_CHANGE
                    GUICtrlSendToDummy($idEditSearch)
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND_Search

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local Static $tText = DllStructCreate("wchar[50]")
    Local Static $pText = DllStructGetPtr($tText)
    Local $tNMHDR, $hWndFrom, $iCode
    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hLV
            Switch $iCode
                Case $LVN_GETDISPINFOW
                    Local $tNMLVDISPINFO = DllStructCreate($tagNMLVDISPINFO, $lParam)
                    If BitAND(DllStructGetData($tNMLVDISPINFO, "Mask"), $LVIF_TEXT) Then
                        Local $iItem = DllStructGetData($tNMLVDISPINFO, "Item")
                        Local $iSubItem = DllStructGetData($tNMLVDISPINFO, "SubItem")

                        ;Search
                        Local $sItem = $aItems[$aSearch[$iItem]][$iSubItem]
                        ;Sort
                        Local $sItem = $aItems[DllStructGetData($tIndex, 1, $iItem + 1)][$iSubItem]


                        DllStructSetData($tText, 1, $sItem)
                        DllStructSetData($tNMLVDISPINFO, "Text", $pText)
                        DllStructSetData($tNMLVDISPINFO, "TextMax", StringLen($sItem))
                    EndIf
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY
 

Вложения

  • LV-Search.au3
    4.2 КБ · Просмотры: 1
  • LV-Sort.au3
    9.6 КБ · Просмотры: 1
  • Cursor.au3
    1.4 КБ · Просмотры: 1
Последнее редактирование:

Norm

Знающий
Сообщения
117
Репутация
15
Вы хотите усидеть на двух стульях одновременно.
Это нужно реализовывать исходя из размеров ListView и поставленных задач.
1. Если список ListView большой, то конечно лучше использовать массив, для поиска в нем же.
Сортировку делать тоже в массиве очищать список ListView в GUI и загонять туда Ваш новый отсортированный массив.
Кстати для этого лучше использовать _GUICtrlListView_AddArray это самый быстрый способ заполнения ListView.

2. Если список ListView большой, но не хочется использовать вариант 1. и скорость заполнения ListView при этом не важна,
то поиск тоже можно делать в массиве, но заполнять список ListView нужно используя GUICtrlCreateListViewItem , но не _GUICtrlListView_AddItem
В этот массив нужно добавить еще один столбик и при заполнении списка ListView параллельно записывать в массив ID каждой созданной строки, типа:
Код:
For $nN = 0 To UBound($aArray)-1
    $nID = GUICtrlCreateListViewItem($aArray[$nN][x]) ; Ваша строка из массива
    $aArray[$nN][0] = $nID ; записаваем ID в массив

При поиске в массиве исключите поиск в столбце с ID, если это будет мешать.
После поиска в массиве смотрите какой ID записан в в найденной строке и далее обращаетесь к этому ID в списке ListView.
Для этого возможно понадобиться конвертировать этот "нативный"-ID в порядковый номер в списке. Используйте для этого:
Код:
$nIx = _GUICtrlListView_FindParam($Liste, $nID)

3. Если список не большой, то можно и сортировать и искать без массива, тем более, что это теперь это можно делать быстрее чем раньше.
 
Последнее редактирование:
Автор
L

liond66

Новичок
Сообщения
102
Репутация
1
Norm, спасибо за Ваш отклик.
Но это больше теория и рассуждения, нежели решение задачи.

Спасибо всем кто заходил и думал над решением.
Я получил оригинальное решение у наших друзей. :rolleyes:
Кому интересно, смотрите здесь.
Тема закрыта.
 

Norm

Знающий
Сообщения
117
Репутация
15
Но это больше теория и рассуждения, нежели решение задачи.
Готовых примеров я не стал делать, но все эти способы я уже реализовал в своем проекте, а тут лишь описал, как это делал.
В Вашем случае возможно именно виртуальный ListView предпочтительней. С ним мне пока не приходилось иметь дело.
 
Верх