powerofos Posted May 5, 2021 Posted May 5, 2021 (edited) 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! @LarsJ expandcollapse popup#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ;#AutoIt3Wrapper_UseX64=y #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_Startup() _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 ) Global $idLV = GUICtrlCreateListView( "", 10, 10, 1650-20, 800-20, $LVS_OWNERDATA+$LVS_OWNERDRAWFIXED, BitOR( $WS_EX_CLIENTEDGE, $LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT ) ) $hLV = GUICtrlGetHandle( $idLV ) ; Virtual listview Reduces flicker For $i = 0 To $iCols - 1 _GUICtrlListView_AddColumn( $idLV, "Col" & $i, 160 ) Next GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") GUICtrlSendMsg( $idLV, $LVM_SETITEMCOUNT, $iRows, 0 ) GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" ) GUISetState( @SW_SHOW ) HotKeySet("{F1}","RowsCount") While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd $pTable -= $iCols * ( @AutoItX64 ? 8 : 4 ) _SQLite_FreeTable( $pTable ) _SQLite_Close() _SQLite_Shutdown() Func RowsCount() ;ToolTip(UBound($array,1)) _ArrayDisplay($array) EndFunc 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 ;#cs ; don't forget, this is BGR, not RGB If $itmState = $bSelected Then ; item is not selected $iBrushColor = 0xCCEECC Else ; item is selected $iBrushColor = 0xEEDDBB EndIf 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) _WinAPI_DeleteObject($aBrush[0]) ;#ce ;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 ) #cs ; 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) #ce Next EndSwitch EndSwitch ;#ce Return $GUI_RUNDEFMSG 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 #cs 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 ) ) Return #ce 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 ) Next Next ;ToolTip(_GUICtrlListView_GetTopIndex ( $idLV )&" - "&_GUICtrlListView_GetNumberOfWorkAreas( $idLV )&" - "&$iFrom&" - "&$iTo) $array = $aDisplay Return EndSwitch EndSwitch Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc 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] EndFunc 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 EndFunc SQL - Virtual listview custom draw.rarFetching info... Edited May 5, 2021 by powerofos
LarsJ Posted May 8, 2021 Posted May 8, 2021 I'll try to take a closer look at the problem, but I need some time. 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
LarsJ Posted May 9, 2021 Posted May 9, 2021 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. 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
powerofos Posted May 13, 2021 Author Posted May 13, 2021 (edited) 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! @Larsj Image Virtual Listview(WM_DrawItem).rarFetching info... Edited May 13, 2021 by powerofos
LarsJ Posted May 13, 2021 Posted May 13, 2021 The basic part of the code seems to be OK so there isn't much I would change about it. 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