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.rar 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).rar 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