Что нового

Элементы GUI Виртуальный список (ListView) на основе базы данных (SQLite), как покрасить по условию

pvnn

Осваивающий
Сообщения
305
Репутация
32
В зависимости от значений в виртуальном списке, необходимо его покрасить, например, если первая колонка = "error", то необходимо покрасить строку цветом.

Для сведения:
1. Строки для отображения в виртуальном списке создаются в $LVN_ODCACHEHINT - Кэш-массив $aResult
2. В $LVN_GETDISPINFOW данные из Кэш-массива $aResult непосредственно отображаются в виртуальном списке
3. Покраска строк происходит в $CDDS_ITEMPREPAINT

Проблема в том, что при покраске списка в $CDDS_ITEMPREPAINT, не удается правильно обратиться к источнику виртуального списка (базе данных), т.е. к кэш-массиву $aResult.
Пытался использовать переменную $n для этого, но до конца не получилось.
Например, при первом старте программы, при прорисовке строк в виртуальном списке, строка, где первая колонка = "error" красится.
Но если кликнуть покрашенную строку, а потом строку выше то выделение пропадает.
Я вижу (ConsoleWrite), что переменная $n (строка кэш-массива $aResult) не совпадает с индексом $iItem в $LVN_ODCACHEHINT, но не пойму как это исправить
Если кликнуть по покрашенной строке, потом кликнуть строку ниже, потом кликнуть снова первоначальную, то будет покрашено две строки, все по той же причине.

* При первом запуске примера, создается база данных в SQLite

Код:
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include <SQLite.au3>
#include <Timers.au3>
#include <Color.au3>

; Create structure $tagNMLVCACHEHINT
Global Const $tagNMLVCACHEHINT = $tagNMHDR & ";int iFrom;int iTo"
Global $hListView



; DB
$sFileDB = @ScriptDir & '\Data.db'
$TableName='lvdata'


; Load SQLite3.dll
_SQLite_Startup()
If @error Then Exit MsgBox(16, "SQLite Error", "SQLite3.dll Can't be Loaded")


; Create DB if not exist
If Not FileExists($sFileDB) Then ; Recreate data base
  TrayTip('Создание БД','Подождите...',30,1)
  $hStarttime=_Timer_Init()
  _SQLite_Open( $sFileDB )
  _SQLite_Exec( -1, "PRAGMA synchronous = OFF;" )

  $sExec = 'Create Table lvdata (' & _
      '[item_id] INTEGER PRIMARY KEY AUTOINCREMENT,' & _
      '[f1] TEXT,' & _
      '[f2] TEXT,' & _
      '[f3] TEXT,' & _
      '[f4] TEXT,' & _
      '[f5] TEXT);'
  _SQLite_Exec( -1, $sExec )
  _SQLite_Exec( -1, "BEGIN TRANSACTION;" )

  Local $Row=500000
  Local $sData='test_value'
  Local $sValues=''
  Local $j=1
  For $i=1 To $Row
    $sValues&= "(" & $i & _
               ",'" & $sData & "'" & _
               ",'" & $sData & "'" & _
               ",'" & $sData & "'" & _
               ",'" & $sData & "'" & _
               ",'" & $sData & "')"
    ; Добавляем порциями по 100 строк для скорости
    If $j=100 Then
        _SQLite_Exec( -1, "INSERT INTO lvdata VALUES " & $sValues & ";" )
        $sValues=''
        $j=1
    Else
        $j+=1
        If $i<>$Row Then $sValues&=','
    EndIf
  Next
  If $sValues<>'' Then _SQLite_Exec( -1, "INSERT INTO lvdata VALUES " & $sValues & ";" )
  _SQLite_Exec( -1, "COMMIT TRANSACTION;" )
  _SQLite_Close( -1 )

  ConsoleWrite(@CRLF&'Create DB, create strings, fill table: '&_Timer_Diff($hStarttime)/1000&@CRLF)
  TrayTip('','',Default)
EndIf



; Open DB
$hDB=_SQLite_Open($sFileDB)
If @error Then Exit MsgBox(16, "SQLite Error", "Can't create a memory Database!")


; Test - Specially delete 5 records
_SQLite_Exec($hDB, 'Delete From '&$TableName&' Where item_id > 5 And item_id <= 10 ;')

; Test - for highlight color
_SQLite_Exec($hDB, 'UPDATE '&$TableName&' SET f1='&_SQLite_FastEscape('error')&' Where item_id = 5 ;')


; После удаления из таблицы записей (item_id > 5 и item_id <= 10), то первичный ключ будет не по порядку
; Для правильного отображения в ListView необходимо установить уникальную связь между строками в списке и строками в таблице
;  1. Создать БД DisplayMemDb в памяти,
;  2. Создать таблицу в памяти tRowRelation, UpdateVirtualListView()
;  3. "INNER JOIN" в сообщении $LVN_ODCACHEHINT в WM_NOTIFY()
; Create memory database DisplayMemDb
_SQLite_Exec($hDB, "ATTACH DATABASE ':memory:' AS DisplayMemDb;")




; GUI
$Form1 = GUICreate("Virtual ListViews", 520, 524, -1, -1)
; Virtual ListViews - $LVS_OWNERDATA, Reduces flicker - $LVS_EX_DOUBLEBUFFER
$ListView1 = GUICtrlCreateListView("", 8, 16, 500, 462, BitOR($LVS_OWNERDATA,$LVS_SHOWSELALWAYS), BitOR( $LVS_EX_GRIDLINES, $WS_EX_CLIENTEDGE, $LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT ))
$hListView = GUICtrlGetHandle($ListView1) ; Handle
; Add Columns
For $i = 0 To 5
    _GUICtrlListView_AddColumn( $hListView, "Col" & $i, 75 )
Next
$Button1 = GUICtrlCreateButton("Button1", 200, 488, 75, 25)

GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )
GUISetState(@SW_SHOW)

UpdateVirtualListView()

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit

        Case $Button1

    EndSwitch
WEnd



; Update Virtual ListView
Func UpdateVirtualListView($SelectedPrimaryKey='')
    _GUICtrlListView_BeginUpdate($hListView)

    ; Delete Table tRowRelation in memory database DisplayMemDb
    _SQLite_Exec(-1,"DROP TABLE IF EXISTS DisplayMemDb.tRowRelation;")
    ; Create Table tRowRelation in memory database DisplayMemDb
    Local $sExec='CREATE TABLE DisplayMemDb.tRowRelation AS SELECT item_id FROM lvdata;'
    _SQLite_Exec(-1,$sExec)

    ; Update Virtual ListView
    GUICtrlSendMsg( $ListView1, $LVM_SETITEMCOUNT, DB_Count(), 0 )

    _GUICtrlListView_EndUpdate($hListView)
EndFunc





Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $tNMHDR, $hWndFrom, $iIDFrom, $iCode
    Local $tInfo, $iIndex, $iSubItem

    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")

    $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam)
    $iIndex = DllStructGetData($tInfo, "Index")
    $iSubItem = DllStructGetData($tInfo, "SubItem")

    Local Static $aResult, $iRows, $iFrom
    Local Static $n

    Switch $hWndFrom
        Case $hListView
            Switch $iCode
                ; Уведомления $LVN_ODCACHEHINT и структура $tagNMLVCACHEHINT используется для вставки строк в кэш массива прежде чем они отобразятся с сообщениями $LVN_GETDISPINFO
                Case $LVN_ODCACHEHINT
                    Local $tNMLVCACHEHINT = DllStructCreate( $tagNMLVCACHEHINT, $ilParam ), $sSQL, $iColumns
                    $iFrom = DllStructGetData( $tNMLVCACHEHINT, "iFrom" )
                    ; lvdata - название таблицы в БД
                    ; item_id - первичный ключ
                    Local $sSQL = "SELECT lvdata.* FROM lvdata " & _
                    "INNER JOIN tRowRelation ON tRowRelation.item_id = lvdata.item_id " & _
                    "WHERE tRowRelation.rowid BETWEEN " & $iFrom + 1 & _
                    " And " & DllStructGetData( $tNMLVCACHEHINT, "iTo" ) + 1 & ";"
                    ; Создает двумерный массив $aResult, содержащий имена столбцов и данные выполненного запроса
                    ; $aResult является кэшом массива. Цель кэша - ограничить число строк SELECT выборки - это максимальное количество пунктов, которое может поместится в данном ListView
                    _SQLite_GetTable2d( -1, $sSQL, $aResult, $iRows, $iColumns )
                    ConsoleWrite(' --- UBound = '&UBound($aResult)&@CRLF)
                    $n=1

                ; View Records
                Case $LVN_GETDISPINFOW
                    ; Установить размерность данных в ячейках для виртуального списка
                    Local Static $tText = DllStructCreate("wchar[4094]") ; Колонка в ListView ограничена 4094 символами, поэтому нет смысла указывать больше
                    Local Static $pText = DllStructGetPtr($tText)         ; Указатель на структуру
                    Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $ilParam )
                    If BitAND( DllStructGetData( $tNMLVDISPINFO, "Mask" ), $LVIF_TEXT ) Then
                        Local $nRow = DllStructGetData( $tNMLVDISPINFO, "Item" ) - $iFrom + 1 ; number row
                        Local $nCol = DllStructGetData($tNMLVDISPINFO, "subitem")            ; number column
                        If $nRow > 0 And $nRow < $iRows + 1 Then
                            Local $sItem = $aResult[$nRow][DllStructGetData($tNMLVDISPINFO,"SubItem")]
                            DllStructSetData( $tText, 1, $sItem ) ; Текст в ячейке
                            DllStructSetData( $tNMLVDISPINFO, "Text", $pText ) ; Указатель на строку, содержащую текст элемента
                            DllStructSetData( $tNMLVDISPINFO, "TextMax", StringLen( $sItem ) ) ; Длина строки. Количество байтов в буфере, на которое указывает текст, включая ограничитель строки
                        EndIf
                    EndIf


                Case $NM_CUSTOMDRAW
                    ; Флаг изменений цвета в списке
                    Local Static $DRAWChange=0

                    Local $tNMLVCD = DllStructCreate($tagNMLVCUSTOMDRAW, $ilParam)
                    Local $iDrawStage = DllStructGetData($tNMLVCD, 'dwDrawStage')
                    Local $iItem = DllStructGetData($tNMLVCD, 'dwItemSpec')        ; Индекс пункта
                    Local $iSubItem = DllStructGetData($tNMLVCD, 'iSubItem')    ; Индекс подпункта

                    Switch $iDrawStage
                        Case $CDDS_PREPAINT
                            Return $CDRF_NOTIFYITEMDRAW

                        Case $CDDS_ITEMPREPAINT
                            ; Вернуть значения по умолчанию, если были изменен: цвет
                            If $DRAWChange=1 Then
                                DllStructSetData($tNMLVCD, 'clrText', 0x000000)     ; Black Текст
                                DllStructSetData($tNMLVCD, 'clrTextBk', 0xFFFFFF)     ; White Фон
                                $DRAWChange=0 ; Флаг изменений цвета или шрифта в списке
                            EndIf

                            ; Покрасить строку
                            If $iRows>0 And $n<$iRows+1 Then
                                ConsoleWrite('=== n = '&$n&'  -> '&$aResult[$n][0]&'  Index =  '&$iItem &@CRLF)
                                If $aResult[$n][1]='error' Then ; Первая колонка
                                    ConsoleWrite('  красим ---> '&$aResult[$n][0]&'  Index =  '&$iItem &@CRLF)
                                    DllStructSetData($tNMLVCD, 'clrTextBk', _ColorSetCOLORREF(_ColorGetRGB(0xffffb5)))
                                    $DRAWChange=1
                                EndIf
                                $n+=1
                            EndIf
                            Return $CDRF_NOTIFYSUBITEMDRAW

                        Case BitOR($CDDS_ITEMPREPAINT, $CDDS_SUBITEM)
                            ; Красим подпункты
                            Return $CDRF_NEWFONT
                    EndSwitch
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc


; Number of rows
Func DB_Count()
    Local $aRow, $iRows = 0
    _SQLite_QuerySingleRow( -1, "SELECT COUNT(*) FROM lvdata LIMIT 1;", $aRow )
    If IsArray( $aRow ) Then $iRows = $aRow[0] + 1
    Return $iRows
EndFunc
 

Вложения

  • sqlite3.7z
    328.7 КБ · Просмотры: 2
Верх