footswitch Posted July 20, 2011 Share Posted July 20, 2011 (edited) Hi there,Since finding I've been experimenting with ListView customized, row-by-row coloring.I hope the example below - which took me a while to extract from my script - can be of help to someone in the future.Still, I have some questions / unresolved issues that lie over additional aspects of the pursued customization.I've commented stuff around in order to get some logic out of it, but some things are currently beyond my knowledge / comprehension.So, if someone could lend a hand, I'd be most grateful.Problems / Questions as explained in the example:PROBLEM #1: When a subitem is changed, the row isn't fully redrawn (only that subitem is) EDIT: Use _GUICtrlListView_RedrawItems($hWnd, $iFirst, $iLast)PROBLEM #2: Alignment doesn't work as expected for the first item of each row (the item text is centered horizontally in relation to the entire width of the ListView)QUESTION #1: How to paint the background of subitems individually? EDIT: check post belowQuestions not in the example:1. How to enable click on column header for sorting the items, with the little arrow included in the header? Can't seem to make it work... Please note: my ListView starts empty with no headers, and the items are loaded/changed/deleted several times.2. How to customize the item's text color? Would it be possible to set the text color for a single item/subitem? EDIT: check post below3. Checkboxes with ownerdraw: is there an easy workaround? (this one isn't critical for my current needs, but could be useful in the future) EDIT: check post belowHere's the example script:expandcollapse popup#include <GuiConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include <WinAPI.au3> 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 $hGUI = GUICreate("Test GUI", 400, 300) ; not using _GUICtrlListView_Create() because the ListView is inside a Tab Item ; (using GUICtrlCreateListView() shows/hides automatically when changing between Tab Items). ; Also in WM_DRAWITEM we look for the ControlID, not the Window Handle $hGUI_listview = GUICtrlCreateListView( "Items|SubItems|Column index 2", 10, 10, 280, 180, _ $LVS_REPORT + $LVS_SINGLESEL + $LVS_SHOWSELALWAYS + $LVS_OWNERDRAWFIXED, _ ; control styles $LVS_EX_FULLROWSELECT + $LVS_EX_GRIDLINES) ; control extended styles; + $LVS_EX_CHECKBOXES doesn't work with OWNERDRAW...? GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") GUISetState() For $i = 1 To 150 _GUICtrlListView_AddItem($hGUI_listview, "Item " & $i) _GUICtrlListView_AddSubItem($hGUI_listview, $i - 1, "Random " & Random(0,$i,1), 1) _GUICtrlListView_AddSubItem($hGUI_listview, $i - 1, $i, 2) Next ; PROBLEM #1 : change some sub-items, as you can see they're not fully redrawn. ; Not until you force it manually by hiding and then showing: ; scroll down/up, minimize/restore, move the window outside of the screen area...). Sleep(2000) _GUICtrlListView_SetItemText($hGUI_listview,0,"4",2) Sleep(2000) _GUICtrlListView_SetItemText($hGUI_listview,5,"4",2) Sleep(2000) _GUICtrlListView_SetItemText($hGUI_listview,7,"4",2) ; ==>> END OF PROBLEM #1 ---------------------------------------------------------- Do Until GUIGetMsg() = $GUI_EVENT_CLOSE #region GUI Owner Draw Callback Func __WM_DRAWITEM_GetColor($hListView_local,$itmID) ; internal function that I use to determine the color for the row. ; let's pretend these are the calculations. ; don't forget, this is BGR, not RGB. Local $aGetItem $aGetItem = _GUICtrlListView_GetItem($hListView_local,$itmID,2) ; Gets info about the last subitem of the row If UBound($aGetItem)>3 Then ; just a safety measure to prevent script error If $aGetItem[3]==4 Then ; if the text of last subitem in this row is = 4 Return 0xDDDDDD ; light grey EndIf EndIf If IsInt($itmID/2) Then ; for instance we're coloring the odd rows light green ($itmID is a zero based index) Return 0xAAEEAA ; light green Else Return 0xFFFFFF ; white EndIf EndFunc Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam) Local $tagDRAWITEMSTRUCT, $iBrushColor, $cID, $itmID, $itmAction, $itmState, $hItm, $hDC, $bSelected $tagDRAWITEMSTRUCT = DllStructCreate( _ "uint cType;" & _ ; ? "uint cID;" & _ ; ControlID of the ListView "uint itmID;" & _ ; ListView item index "uint itmAction;" & _ ; ? "uint itmState;" & _ ; ? with the BitAND operation below, allows seeing if the item is selected "hwnd hItm;" & _ ; ? "hwnd hDC;" & _ ; ? "int itmRect[4];" & _ ; Item rectangle coordinates (entire row) "dword itmData" _ ; ? , $lParam) ; is this the ListView handle? If DllStructGetData($tagDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG $cID = DllStructGetData($tagDRAWITEMSTRUCT, "cID") $itmID = DllStructGetData($tagDRAWITEMSTRUCT, "itmID") $itmAction = DllStructGetData($tagDRAWITEMSTRUCT, "itmAction") $itmState = DllStructGetData($tagDRAWITEMSTRUCT, "itmState") $hItm = DllStructGetData($tagDRAWITEMSTRUCT, "hItm") $hDC = DllStructGetData($tagDRAWITEMSTRUCT, "hDC") $bSelected = BitAND($itmState, $ODS_SELECTED) Switch $cID ; see which ControlID is requesting OWNERDRAW Case $hGUI_listview ; (there's more than one listview in the original GUI) ; if you want all the ListViews with the same formatting, just comment out the parent Switch-Case-EndSwitch Switch $itmAction Case $ODA_DRAWENTIRE ConsoleWrite("Drawing Item "&$itmID+1&@CRLF) ; debugging ; don't forget, this is BGR, not RGB If $itmState = $bSelected Then ; item is NOT selected $iBrushColor = __WM_DRAWITEM_GetColor($hGUI_listview,$itmID) Else ; item is selected (when an item is selected, always force this color) $iBrushColor = 0xFFCCAA ; light blue EndIf ; QUESTION #1 : paints the whole item with a single color. ----------- ; how to paint different subitems with different colors? ------------- 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]) ; ==>> END OF QUESTION #1 --------------------------------------------- ; draw the respective text for each column of the row $local_alignment=$DT_LEFT ; default text alignment For $i=0 To _GUICtrlListView_GetColumnCount($hGUI_listview)-1 ; 1. get subitem text: Local $iSubItmText = _GUICtrlListView_GetItemText($hGUI_listview, $itmID, $i) ; 2. get subitem coordinates for drawing its respective text Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hGUI_listview, $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("int[4]") DllStructSetData($iSubItmRect, 1, $aSubItmRect[0]+6, 1) ; +6 is left margin (X) DllStructSetData($iSubItmRect, 1, $aSubItmRect[1]+3, 2) ; +3 is upper margin (Y) DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3) DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4) ; 4. draw the subitem text ; PROBLEM #2 -------------------------- #cs If $i==0 Then $local_alignment=$DT_CENTER ; not working... it's centering the item in relation to the entire listview. ; don't know why that happens but the main item is always trickier than the subitems Else $local_alignment=$DT_LEFT EndIf #ce ; ==>> END OF PROBLEM #2 -------------- DllCall("user32.dll", "int", "DrawText", "hwnd", $hDC, "str", $iSubItmText, "int", StringLen($iSubItmText), _ "ptr", DllStructGetPtr($iSubItmRect), "int", $local_alignment) Next EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc #endregion GUI Owner Draw CallbackLooking forward for any remarks you may have on the subject.Thanks again for reading.footswitch Edited July 25, 2011 by footswitch Link to comment Share on other sites More sharing options...
Spiff59 Posted July 22, 2011 Share Posted July 22, 2011 (edited) I believe intercepting the WM_NOTIFY message and manipulating the ListView from that level is much easier? Maybe the following example will be of some use: expandcollapse popup#include <GuiConstants.au3> #include <GuiListView.au3> #include <WindowsConstants.au3> Global $hListView Global $Color[6][2] = [[0xFF00FF, 0xFFFFFF],[0x00FFFF, 0x808000],[0x008080, 0xFFFFFF],[0xFFFFFF, 0x000000],[0x0000FF, 0xC0C0C0]] GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") $Main_GUI = GuiCreate("", 800, 600, -1, -1) $hListView = GuiCtrlCreateListView("", 10, 10, 780, 580, -1, BitOR($LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT)) ;$hListView = GUICtrlGetHandle($hListView) _GUICtrlListView_AddColumn($hListView, "TIME", 40) _GUICtrlListView_AddColumn($hListView, "CHART # ", 70) _GUICtrlListView_AddColumn($hListView, "PATIENT NAME ", 150) GUISetState() For $x = 0 to 4 _GUICtrlListView_AddItem($hListView, "TIME" & $x) _GUICtrlListView_AddSubItem($hListView, $x, "CHART" & $x, 1) _GUICtrlListView_AddSubItem($hListView, $x, "PATIENT" & $x, 2) Next While 1 $msg = GUIGetMsg() Switch $msg Case $GUI_EVENT_CLOSE ExitLoop EndSwitch Wend Exit ;------------------------------------------------------------------------------- Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam) #forceref $hWnd, $iMsg, $iwParam If IsHWnd($hListView) Then $hWndListView = $hListView Else $hWndListView = GUICtrlGetHandle($hListView) EndIf $tNMHDR = DllStructCreate($tagNMHDR, $ilParam) $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom")) $iIDFrom = DllStructGetData($tNMHDR, "IDFrom") $iCode = DllStructGetData($tNMHDR, "Code") Switch $hWndFrom Case $hWndListView Switch $iCode ; Case $LVN_ENDSCROLL ; Force redraw of gridlines lost when scrolling ; $tNMHDR = DllStructCreate("hwnd hWnd;uint cID;int code", $ilParam) ; $hLV = HWnd(DllStructGetData($tNMHDR, "hWnd")) ; _WinAPI_InvalidateRect($hLV) ; Case $NM_DBLCLK ; Process clicks ; $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam) ; $aHit = _GUICtrlListView_SubItemHitTest($hWndListView) ; $ahit[0] = row clicked, $ahit[1] = column clicked ; If $aHit[0] < 0 Then Return $GUI_RUNDEFMSG ; Switch $aHit[1] ; Case 1 ; ShellExecute($ScanPgm, Chr(34) & $PatientPath[$aHit[0]] & Chr(34)) ; EndSwitch Case $NM_CUSTOMDRAW Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $ilParam) Local $iDrawStage = DllStructGetData($tCustDraw, "dwDrawStage") If $iDrawStage = $CDDS_PREPAINT Then Return $CDRF_NOTIFYITEMDRAW If $iDrawStage = $CDDS_ITEMPREPAINT Then Return $CDRF_NOTIFYSUBITEMDRAW Local $iItem = DllStructGetData($tCustDraw, "dwItemSpec") Local $iSubItem = DllStructGetData($tCustDraw, "iSubItem") $iColor = 0x000000 ; reset default $iColorBk = 0xFFFFFF ; reset default If $iItem = 4 Then ; row 4 $iColorBk = 0xC0C0C0 EndIf If $iSubItem = 1 Then ; column 1 $iColor = 0xFF0000 EndIf If $iSubItem = 2 Then ; column 2 $iColor = $Color[$iItem][0] $iColorBk = $Color[$iItem][1] EndIf DllStructSetData($tCustDraw, "clrTextBk", $iColorBk) DllStructSetData($tCustDraw, "clrText", $iColor) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc typos Edited July 22, 2011 by Spiff59 footswitch 1 Link to comment Share on other sites More sharing options...
footswitch Posted July 25, 2011 Author Share Posted July 25, 2011 Wow, that looks so much cleaner. Thank you. This solves QUESTION #1 in the example, and additional Questions 2+3. However, it raises an issue I didn't have before: Would you be kind enough to provide an example on how to draw depending on the item state (selected/unselected)? This is what I've tried inside of your WM_NOTIFY function: Local $iItemState = DllStructGetData($tCustDraw, "uItemState") If BitAND($iItemState,$CDIS_SELECTED) Then ; item is selected ; some code EndIf But this always returns True. Also, this isn't the same as an item being clicked (you can also change selection with the keyboard arrows and/or UDF functions). Thanks again for your input, Spiff59. footswitch Link to comment Share on other sites More sharing options...
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