Jump to content

SQLite Virtual Listview, WM_DRAWITEM questions

Recommended Posts

Hello everyone!

I got a problem in making a SQL Virtual listview image tool,

The images are stored in binary and I need to read and display them in a virtual listview,

Listview style: $LVS_ OWNERDATA and $LVS_ OWNERDRAWFIXED, not Imagelist method

Data Array I got from $LVN_ODCACHEHINT(Virtual listview) is not match when I scrolled up or down listview ctrl, so WM_DRAWITEM report errors

My test script are in rar package below. Thank you very much!




#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6


#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include <SQLite.au3>

Opt( "MustDeclareVars", 1 )

Global Const $ODT_LISTVIEW = 102
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4
Global Const $ODS_SELECTED = 0x0001

Global $pTable, $iCols, $hLV, $array

  _SQLite_Open( "random_data.db" )

Global $iRows
Global $sSQL = "SELECT * FROM RandomData;"
  _SQLite_GetTableEx( -1, $sSQL, $pTable, $iRows, $iCols )
  $pTable += $iCols * ( @AutoItX64 ? 8 : 4 )

  GUICreate( "Virtual ListView", 1650, 800 )

  $hLV = GUICtrlGetHandle( $idLV ) ;                                Virtual listview                          Reduces flicker
  For $i = 0 To $iCols - 1
    _GUICtrlListView_AddColumn( $idLV, "Col" & $i, 160 )


  GUICtrlSendMsg( $idLV, $LVM_SETITEMCOUNT, $iRows, 0 )
  GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )
  GUISetState( @SW_SHOW )

  While 1
    Switch GUIGetMsg()

  $pTable -= $iCols * ( @AutoItX64 ? 8 : 4 )
  _SQLite_FreeTable( $pTable )

Func RowsCount()

Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $wParam

    Local $tagDRAWITEMSTRUCT,  $cID, $itmID, $itmAction,  $hDC
    $tagDRAWITEMSTRUCT = DllStructCreate( _
   "uint cType;" & _
   "uint cID;" & _
   "uint itmID;" & _
   "uint itmAction;" & _
   "uint itmState;" & _
   "hwnd hItm;" & _
   "handle hDC;" & _
   "long itmRect[4];" & _
   "ulong_ptr itmData" _
   , $lParam)
    If DllStructGetData($tagDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG
    $cID = DllStructGetData($tagDRAWITEMSTRUCT, "cID")
    $itmID = DllStructGetData($tagDRAWITEMSTRUCT, "itmID")
    $itmAction = DllStructGetData($tagDRAWITEMSTRUCT, "itmAction")
    $hDC = DllStructGetData($tagDRAWITEMSTRUCT, "hDC")
    Local $itmState = DllStructGetData($tagDRAWITEMSTRUCT, "itmState")
    ;Local $hItm = DllStructGetData($tagDRAWITEMSTRUCT, "hItm")
    Local $bSelected = BitAND($itmState, $ODS_SELECTED),$iBrushColor

    Switch $cID ; will look for ControlID, not window handle.
        Case $idLV
            Switch $itmAction
                Case $ODA_DRAWENTIRE
                    ; don't forget, this is BGR, not RGB
                    If $itmState = $bSelected Then ; item is not selected
                        $iBrushColor = 0xCCEECC
                    Else ; item is selected
                        $iBrushColor = 0xEEDDBB
                    Local $aBrush = DllCall("gdi32.dll", "hwnd", "CreateSolidBrush", "int", $iBrushColor)
                    Local $aBrushOld = _WinAPI_SelectObject($hDC, $aBrush[0])
                    Local $iLeft = DllStructGetData($tagDRAWITEMSTRUCT, "itmRect", 1)
                    DllStructSetData($tagDRAWITEMSTRUCT, "itmRect", $iLeft + 1, 1) ; rectangle coordinates for coloring
                    ;+1 is the left margin
                    _WinAPI_FillRect($hDC, DllStructGetPtr($tagDRAWITEMSTRUCT, "itmRect"), $aBrush[0])
                    _WinAPI_SelectObject($hDC, $aBrushOld)


                    ;For $i = 0 To _GUICtrlListView_GetColumnCount($hLV) - 1
                    For $i = 0 To UBound($array,2) - 1
                        ; 1. get subitem text:
                        Local $iSubItmText = $array[$itmID][$i];_GUICtrlListView_GetItemText($hLV, $itmID, $i)
                        ; 2. get subitem coordinates for drawing its respective text
                        Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hLV, $itmID, $i)
                        ; the function above accepts not only subitems (one-based index), but also main item (index=0)
                        ; 3. pass the coordinates to a DLL struct
                        Local $iSubItmRect = DllStructCreate("long[4]")
                        DllStructSetData($iSubItmRect, 1, $aSubItmRect[0] + 6, 1) ; +6 is left margin (X)
                        DllStructSetData($iSubItmRect, 1, $aSubItmRect[1] + (-2), 2) ; + (-2) is upper margin (Y)
                        DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3)
                        DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4)
                        DllCall("user32.dll", "int", "DrawTextW", "hwnd", $hDC, "wstr", $iSubItmText, "int", StringLen($iSubItmText), _
                            "ptr", DllStructGetPtr($iSubItmRect), "int", $DT_LEFT  )
                        ;_WinAPI_DrawText ( $hDC, $iSubItmText, $iSubItmRect,  $DT_WORD_ELLIPSIS +$DT_LEFT  )

                        ; Load image
Local $hSource = _WinAPI_LoadImage(0, @ScriptDir & '\1.bmp', $IMAGE_BITMAP, 0, 0, $LR_LOADFROMFILE)
Local $hDestDC = _WinAPI_CreateCompatibleDC($hDC)
Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, @DesktopWidth, @DesktopHeight)
Local $hDestSv = _WinAPI_SelectObject($hDestDC, $hBitmap)
        _WinAPI_DrawBitmap($hDC, 10,10, $hSource)

EndFunc   ;==>WM_DRAWITEM

Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Static $iPtrSize = @AutoItX64 ? 8 : 4, $hDLL = DllOpen( "kernel32.dll" ), $aDisplay[100][$iCols], $iFrom, $iTo
  ;Static $tText = DllStructCreate( "wchar[4094]" ), $pText = DllStructGetPtr( $tText )
  ;Static $tagNMLVCACHEHINT = $tagNMHDR & ";int iFrom;int iTo"

  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 Not BitAND( DllStructGetData( $tNMLVDISPINFO, "Mask" ), 1 ) Then Return ; 1 = $LVIF_TEXT
                    Local $iItem = DllStructGetData( $tNMLVDISPINFO, "Item" ) - $iFrom
                    If $iItem < 0 Or $iItem > $iTo - $iFrom Then Return
                    Local $iSubItem = DllStructGetData($tNMLVDISPINFO,"SubItem")
                    Local $sItem = $aDisplay[$iItem][$iSubItem]
                    Local $l = StringLen( $sItem )
                    DllStructSetData( $tText, 1, $sItem )
                    DllStructSetData( $tNMLVDISPINFO, "Text", $pText )
                    DllStructSetData( $tNMLVDISPINFO, "TextMax", ( $l <= 4094 ? $l : 4094 ) )
                Case $LVN_ODCACHEHINT
                    ;Local $tNMLVCACHEHINT = DllStructCreate( $tagNMLVCACHEHINT, $lParam )
                    ;$iFrom = DllStructGetData( $tNMLVCACHEHINT, "iFrom" )
                    ;$iTo = DllStructGetData( $tNMLVCACHEHINT, "iTo" )
                    $iFrom = _GUICtrlListView_GetTopIndex ( $idLV )
                    $iTo = $iFrom+39
                    If $iTo > $iRows-1 Then $iTo = $iRows-1
                    ;ToolTip($iFrom&" - "&$iTo)
                    Local $pFrom, $pStr, $iLen, $tWstr
                    For $i = $iFrom To $iTo
                        $pFrom = $pTable + $i * $iCols * $iPtrSize
                        For $j = 0 To $iCols - 1
                            $pStr = DllStructGetData( DllStructCreate( "ptr", $pFrom + $j * $iPtrSize ), 1 )
                            $iLen = DllCall( $hDLL, "int", "MultiByteToWideChar", "uint", 65001, "dword", 0, "ptr", $pStr, "int", -1, "ptr", 0, "int", 0 )[0]
                            $tWstr = DllStructCreate( "wchar[" & $iLen & "]" )
                            DllCall( $hDLL, "int", "MultiByteToWideChar", "uint", 65001, "dword", 0, "ptr", $pStr, "int", -1, "struct*", $tWstr, "int", $iLen )
                            $aDisplay[$i-$iFrom][$j] = DllStructGetData( $tWstr, 1 )
                    ;ToolTip(_GUICtrlListView_GetTopIndex ( $idLV )&" - "&_GUICtrlListView_GetNumberOfWorkAreas( $idLV )&" - "&$iFrom&" - "&$iTo)

                    $array = $aDisplay
    #forceref $hWnd, $iMsg, $wParam

Func _SQLite_GetTableEx( $hDB, $sSQL, ByRef $pTable, ByRef $iRows, ByRef $iCols )
  If __SQLite_hChk($hDB, 2) Then Return SetError(@error, 0, $SQLITE_MISUSE)
  Local $tSQL8 = __SQLite_StringToUtf8Struct($sSQL)
  If @error Then Return SetError(3, @error, 0)
  Local $avRval = DllCall($__g_hDll_SQLite, "int:cdecl", "sqlite3_get_table", _
    "ptr", $hDB, _       ; An open database
    "struct*", $tSQL8, _ ; SQL to be executed
    "ptr*", 0, _         ; Results of the query
    "int*", 0, _         ; Number of result rows
    "int*", 0, _         ; Number of result columns
    "long*", 0)          ; Error msg written here
  If @error Then Return SetError(1, @error, $SQLITE_MISUSE) ; DllCall error
  $pTable = $avRval[3]
  $iRows = $avRval[4]
  $iCols = $avRval[5]

Func _SQLite_FreeTable( $pTable )
  DllCall($__g_hDll_SQLite, "int:cdecl", "sqlite3_free_table", "ptr", $pTable)
  If @error Then Return SetError(1, @error, $SQLITE_MISUSE) ; DllCall error



SQL - Virtual listview custom draw.rar

Edited by powerofos
Link to comment
Share on other sites

Link to comment
Share on other sites

You cannot combine virtual listview functionality (LVS_OWNERDATA) with owner drawn listview functionality (LVS_OWNERDRAWFIXED) the way you want to.

If you want to use an owner drawn listview, the only purpose of setting the LVS_OWNERDATA style (virtual listview) is to prevent data from being stored directly in the listview structures to avoid issues as described in this post. However, LVN_ODCACHEHINT notifications can in no way be combined with WM_DRAWITEM messages. It simply will not work. These are two completely different techniques that cannot be combined.

If you want to use a virtual listview and at the same time extract data from the SQLite database with the sqlite3_get_table() function that stores output in a C-array, then there is absolutely no point in using LVN_ODCACHEHINT to store data in another memory cache. The C-array is already a memory cache, and no more caching is needed.

If the purpose of your code is to display images in a listview where the images are stored as binary data in a SQLite database, then I would suggest that you first start by figuring out how to display just a single image from the database in a owner drawn listview. And once you have figured that out, you can figure out how to handle many images.

Link to comment
Share on other sites

Hi Larsj,

Thank you for your reply!

Through your explanation,  I don't think I have the ability to deal with this problem, so I changed to another way.
Instead of reading data directly from the database, I read the data to the array first, and then use the drawitem message to draw it down,include the image.

The following rar package is an example that I modified from yours,  can you help me improve it or give me any suggestion?

Thank you!




Image Virtual Listview(WM_DrawItem).rar

Edited by powerofos
Link to comment
Share on other sites

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...