Jump to content

Multi-line listview items with rows of different heights


Recommended Posts

As the title says i want to create a listview with rows that have diffrent heights.

@LarsJ said that to do this i will have to use $WM_MEASUREITEM messages, but i don't know how to start it. Does anyone here could help me please? Thanks!

dasdsa.png

Link to comment
Share on other sites

Post 3

It appears that only a listbox supports varying row heights. You can test this for a start:

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

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListBox.au3>

Global Const $Gdi32dll = DllOpen( "gdi32.dll" )
Global Const $User32dll = DllOpen( "user32.dll" )

; DRAWITEMSTRUCT
Global Const $tagDRAWITEMSTRUCT ="uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;dword rcItem[4];ptr itemData"

; CtlType
Global Const $ODT_LISTBOX = 2

; itemAction
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4

; itemState
Global Const $ODS_SELECTED = 0x1

Global $iRowHeight = 16, $iMargin = 4
Global $aRows = [ [ 2, "Row 0", "    Subrow 2"                 ], _
                  [ 3, "Row 1", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 2", "    Subrow 2"                 ], _
                  [ 1, "Row 3"                                 ], _
                  [ 3, "Row 4", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 5", "    Subrow 2"                 ], _
                  [ 3, "Row 6", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 7", "    Subrow 2"                 ], _
                  [ 1, "Row 8"                                 ], _
                  [ 3, "Row 9", "    Subrow 2", "    Subrow 3" ] ]

Example()

Func Example()
  GUICreate( "Ownerdrawn Listbox", 300, 300 )
  Local $idListBox = GUICtrlCreateList( "", 4, 4, 292, 292, $WS_VSCROLL+$LBS_OWNERDRAWVARIABLE )

  ; Set row height
  For $i = 0 To UBound( $aRows ) - 1
    _GUICtrlListBox_AddString( $idListBox, $aRows[$i][1] )
    _GUICtrlListBox_SetItemHeight( $idListBox, $aRows[$i][0] * $iRowHeight + $iMargin, $i )
  Next

  GUIRegisterMsg( $WM_DRAWITEM, "WM_DRAWITEM" )

  GUISetState( @SW_SHOW )

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  GUIDelete()
EndFunc

Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam )
  Local $tDRAWITEMSTRUCT = DllStructCreate( $tagDRAWITEMSTRUCT, $lParam )
  Local $CtlType = DllStructGetData( $tDRAWITEMSTRUCT, "CtlType" )
  If $CtlType <> $ODT_LISTBOX Then Return $GUI_RUNDEFMSG

  Local $itemID = DllStructGetData( $tDRAWITEMSTRUCT, "itemID" )
  Local $itemAction = DllStructGetData( $tDRAWITEMSTRUCT, "itemAction" )
  ;Local $itemState = DllStructGetData( $tDRAWITEMSTRUCT, "itemState" )
  Local $hDC = DllStructGetData( $tDRAWITEMSTRUCT, "hDC" )

  Local $iBrushColor = ColorConvert( 0xEAEE97 )
  Local $iTextColor = 0x000000, $DT_LEFT = 0

  Switch $itemAction
    Case $ODA_DRAWENTIRE, $ODA_SELECT
      ; Item rectangle
      Local $tRECT = DllStructCreate( $tagRECT, DllStructGetPtr( $tDRAWITEMSTRUCT, "rcItem" ) )

      ; Background color
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Bottom" ) - 1 )
      Local $aBrush = DLLCall( $Gdi32dll, "hwnd", "CreateSolidBrush", "int", $iBrushColor )
      Local $aBrushOld = DLLCall( $Gdi32dll, "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $aBrush[0] )
      DLLCall( $User32dll, "int", "FillRect", "hwnd", $hDC, "struct*", $tRECT, "hwnd", $aBrush[0] )
      DLLCall( $Gdi32dll, "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $aBrushOld[0] )
      DLLCall( $Gdi32dll, "int", "DeleteObject", "hwnd", $aBrush[0] )
      DLLCall( $Gdi32dll, "int", "SetBkMode", "hwnd", $hDC, "int", 1 ) ; 1 = $TRANSPARENT

      ; Text color
      DllCall( $Gdi32dll, "int", "SetTextColor", "hwnd", $hDC, "int", $iTextColor )

      ; Left margin
      DllStructSetData( $tRECT, "Left",   DllStructGetData( $tRECT, "Left" ) + 4 )

      ; Item text 1
      Local $sItemText = $aRows[$itemID][1]
      DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Top"  ) + 2 )
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"  ) + $iRowHeight )
      DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )

      ; Item text 2
      If $aRows[$itemID][2] Then
        $sItemText = $aRows[$itemID][2]
        DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Bottom" ) )
        DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"    ) + $iRowHeight )
        DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )
      EndIf

      ; Item text 3
      If $aRows[$itemID][3] Then
        $sItemText = $aRows[$itemID][3]
        DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Bottom" ) )
        DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"    ) + $iRowHeight )
        DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )
      EndIf
  EndSwitch

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

;RGB to BGR or BGR to RGB
Func ColorConvert($iColor)
  Return BitOR(BitAND($iColor, 0x00FF00), BitShift(BitAND($iColor, 0x0000FF), -16), BitShift(BitAND($iColor, 0xFF0000), 16))
EndFunc

 

Edited by LarsJ
Code update
Link to comment
Share on other sites

20 hours ago, FrancescoDiMuro said:

Hi @x_bennY:)
The first suggestion I give to you, is just try some code, and then post it here :)
We can't see what you are doing, if you don't do anything, and you don't post anything here :)

Best Regards.

Hey! The problem is that i don't know how to start... hahaha

 

10 hours ago, LarsJ said:

It appears that only a listbox supports varying row heights. You can test this for a start:

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

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListBox.au3>

Global Const $Gdi32dll = DllOpen( "gdi32.dll" )
Global Const $User32dll = DllOpen( "user32.dll" )

; DRAWITEMSTRUCT
Global Const $tagDRAWITEMSTRUCT ="uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;dword rcItem[4];ptr itemData"

; CtlType
Global Const $ODT_LISTBOX = 2

; itemAction
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4

; itemState
Global Const $ODS_SELECTED = 0x1

Global $iRowHeight = 16, $iMargin = 4
Global $aRows = [ [ 2, "Row 0", "    Subrow 2"                 ], _
                  [ 3, "Row 1", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 2", "    Subrow 2"                 ], _
                  [ 1, "Row 3"                                 ], _
                  [ 3, "Row 4", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 5", "    Subrow 2"                 ], _
                  [ 3, "Row 6", "    Subrow 2", "    Subrow 3" ], _
                  [ 2, "Row 7", "    Subrow 2"                 ], _
                  [ 1, "Row 8"                                 ], _
                  [ 3, "Row 9", "    Subrow 2", "    Subrow 3" ] ]

Example()

Func Example()
  GUICreate( "Ownerdrawn Listbox", 300, 300 )
  Local $idListBox = GUICtrlCreateList( "", 4, 4, 292, 292, $WS_VSCROLL+$LBS_OWNERDRAWVARIABLE )

  ; Set row height
  For $i = 0 To UBound( $aRows ) - 1
    _GUICtrlListBox_AddString( $idListBox, $aRows[$i][1] )
    _GUICtrlListBox_SetItemHeight( $idListBox, $aRows[$i][0] * $iRowHeight + $iMargin, $i )
  Next

  GUIRegisterMsg( $WM_DRAWITEM, "WM_DRAWITEM" )

  GUISetState( @SW_SHOW )

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  GUIDelete()
EndFunc

Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam )
  Local $tDRAWITEMSTRUCT = DllStructCreate( $tagDRAWITEMSTRUCT, $lParam )
  Local $CtlType = DllStructGetData( $tDRAWITEMSTRUCT, "CtlType" )
  If $CtlType <> $ODT_LISTBOX Then Return $GUI_RUNDEFMSG

  Local $itemID = DllStructGetData( $tDRAWITEMSTRUCT, "itemID" )
  Local $itemAction = DllStructGetData( $tDRAWITEMSTRUCT, "itemAction" )
  ;Local $itemState = DllStructGetData( $tDRAWITEMSTRUCT, "itemState" )
  Local $hDC = DllStructGetData( $tDRAWITEMSTRUCT, "hDC" )

  Local $iBrushColor = ColorConvert( 0xEAEE97 )
  Local $iTextColor = 0x000000, $DT_LEFT = 0

  Switch $itemAction
    Case $ODA_DRAWENTIRE, $ODA_SELECT
      ; Item rectangle
      Local $tRECT = DllStructCreate( $tagRECT, DllStructGetPtr( $tDRAWITEMSTRUCT, "rcItem" ) )

      ; Background color
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Bottom" ) - 1 )
      Local $aBrush = DLLCall( $Gdi32dll, "hwnd", "CreateSolidBrush", "int", $iBrushColor )
      Local $aBrushOld = DLLCall( $Gdi32dll, "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $aBrush[0] )
      DLLCall( $User32dll, "int", "FillRect", "hwnd", $hDC, "struct*", $tRECT, "hwnd", $aBrush[0] )
      DLLCall( $Gdi32dll, "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $aBrushOld[0] )
      DLLCall( $Gdi32dll, "int", "DeleteObject", "hwnd", $aBrush[0] )
      DLLCall( $Gdi32dll, "int", "SetBkMode", "hwnd", $hDC, "int", 1 ) ; 1 = $TRANSPARENT

      ; Text color
      DllCall( $Gdi32dll, "int", "SetTextColor", "hwnd", $hDC, "int", $iTextColor )

      ; Left margin
      DllStructSetData( $tRECT, "Left",   DllStructGetData( $tRECT, "Left" ) + 4 )

      ; Item text 1
      Local $sItemText = $aRows[$itemID][1]
      DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Top"  ) + 2 )
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"  ) + $iRowHeight )
      DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )

      ; Item text 2
      If $aRows[$itemID][2] Then
        $sItemText = $aRows[$itemID][2]
        DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Bottom" ) )
        DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"    ) + $iRowHeight )
        DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )
      EndIf

      ; Item text 3
      If $aRows[$itemID][3] Then
        $sItemText = $aRows[$itemID][3]
        DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Bottom" ) )
        DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"    ) + $iRowHeight )
        DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )
      EndIf
  EndSwitch

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

;RGB to BGR or BGR to RGB
Func ColorConvert($iColor)
  Return BitOR(BitAND($iColor, 0x00FF00), BitShift(BitAND($iColor, 0x0000FF), -16), BitShift(BitAND($iColor, 0xFF0000), 16))
EndFunc

 

You're awesome! I will study this code and try to do what i need, If I have any questions I'll ask here. a lot of thx!!

---

I need to write a number of items according to what the user selects, what I need to do to be able to change text and height of items all the time? Your code is nice, that's exactly what I need. How the code works? Wich line that i add i will need to "do that thing" in the "WM_DRAWITEM"?

Edited by x_bennY
Link to comment
Share on other sites

Post 5

Slightly more flexible code. Added highlight of selected item. Demonstrates listbox refresh.


How the code works? The $LBS_OWNERDRAWVARIABLE style means that the operating system sends WM_DRAWITEM messages when the listbox has to be updated. These messages are captured by AutoIt's internal message management system. For each message the management system executes the WM_DRAWITEM function.

To delete and add rows, update texts and change row heights, use _GUICtrlListBox_ and array functions. Normally you'll not update the code directly in the WM_DRAWITEM function.

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

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListBox.au3>

Global Const $Gdi32dll = DllOpen( "gdi32.dll" )
Global Const $User32dll = DllOpen( "user32.dll" )

; DRAWITEMSTRUCT
Global Const $tagDRAWITEMSTRUCT ="uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;dword rcItem[4];ptr itemData"

; CtlType
Global Const $ODT_LISTBOX = 2

; itemAction
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4

; itemState
Global Const $ODS_SELECTED = 0x1

Global $iRowHeight = 16, $iMargin = 4
Global $aRows = [ [ 2, "Row 0", "    Subrow 2"                                 ], _
                  [ 3, "Row 1", "    Subrow 2", "    Subrow 3"                 ], _
                  [ 2, "Row 2", "    Subrow 2"                                 ], _
                  [ 1, "Row 3"                                                 ], _
                  [ 4, "Row 4", "    Subrow 2", "    Subrow 3", "    Subrow 4" ], _
                  [ 2, "Row 5", "    Subrow 2"                                 ], _
                  [ 3, "Row 6", "    Subrow 2", "    Subrow 3"                 ], _
                  [ 2, "Row 7", "    Subrow 2"                                 ], _
                  [ 1, "Row 8"                                                 ], _
                  [ 3, "Row 9", "    Subrow 2", "    Subrow 3"                 ] ]

Example()

Func Example()
  GUICreate( "Ownerdrawn Listbox", 300, 340 )
  Local $idListBox = GUICtrlCreateList( "", 10, 10, 280, 280, $WS_VSCROLL+$LBS_OWNERDRAWVARIABLE ), $hListBox = GUICtrlGetHandle( $idListBox )
  Local $idButton = GUICtrlCreateButton( "Modify row 4", 10, 300, 280, 30 )

  ; Fill listbox rows and set row height
  ; This tells WM_DRAWITEM function how many rows to draw
  For $i = 0 To UBound( $aRows ) - 1
    _GUICtrlListBox_AddString( $idListBox, $aRows[$i][1] )
    _GUICtrlListBox_SetItemHeight( $idListBox, $aRows[$i][0] * $iRowHeight + $iMargin, $i )
  Next

  GUIRegisterMsg( $WM_DRAWITEM, "WM_DRAWITEM" )

  GUISetState( @SW_SHOW )

  Local $n = 0
  While 1
    Switch GUIGetMsg()
      Case $idButton
        $n += 1
        $aRows[4][1] = "Modified row 4 (" & $n & ")"
        _WinAPI_InvalidateRect( $hListBox ) ; Refresh
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  GUIDelete()
EndFunc

Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam )
  Local $tDRAWITEMSTRUCT = DllStructCreate( $tagDRAWITEMSTRUCT, $lParam )
  Local $CtlType = DllStructGetData( $tDRAWITEMSTRUCT, "CtlType" )
  If $CtlType <> $ODT_LISTBOX Then Return $GUI_RUNDEFMSG

  Local $itemID = DllStructGetData( $tDRAWITEMSTRUCT, "itemID" )
  Local $itemAction = DllStructGetData( $tDRAWITEMSTRUCT, "itemAction" )
  Local $itemState = DllStructGetData( $tDRAWITEMSTRUCT, "itemState" )
  Local $hDC = DllStructGetData( $tDRAWITEMSTRUCT, "hDC" )

  Local $iBrushColor, $iTextColor, $DT_LEFT = 0

  Switch $itemAction
    Case $ODA_DRAWENTIRE, $ODA_SELECT
      ; Item state
      If $itemState = $ODS_SELECTED Then
        $iBrushColor = _WinAPI_GetSysColor( $COLOR_HIGHLIGHT )
        $iTextColor = 0xFFFFFF
      Else
        $iBrushColor = ColorConvert( 0xEAEE97 )
        $iTextColor = 0x000000
      EndIf

      ; Item rectangle
      Local $tRECT = DllStructCreate( $tagRECT, DllStructGetPtr( $tDRAWITEMSTRUCT, "rcItem" ) )

      ; Background color
      DLLCall( $Gdi32dll, "int", "SetBkMode", "hwnd", $hDC, "int", 1 ) ; 1 = $TRANSPARENT
      Local $aBrush = DLLCall( $Gdi32dll, "hwnd", "CreateSolidBrush", "int", $iBrushColor )
      DLLCall( $Gdi32dll, "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $aBrush[0] )
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Bottom" ) - 1 )
      DLLCall( $User32dll, "int", "FillRect", "hwnd", $hDC, "struct*", $tRECT, "hwnd", $aBrush[0] )
      DLLCall( $Gdi32dll, "int", "DeleteObject", "hwnd", $aBrush[0] )

      ; Text color
      DllCall( $Gdi32dll, "int", "SetTextColor", "hwnd", $hDC, "int", $iTextColor )

      ; Left margin
      DllStructSetData( $tRECT, "Left",   DllStructGetData( $tRECT, "Left" ) + 4 )

      ; Item text 1
      Local $sItemText = $aRows[$itemID][1]
      DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Top"  ) + 2 )
      DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"  ) + $iRowHeight )
      DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )

      ; Item text 2 - last column
      For $i = 2 To UBound( $aRows, 2 ) - 1
        If $aRows[$itemID][$i] Then
          $sItemText = $aRows[$itemID][$i]
          DllStructSetData( $tRECT, "Top",    DllStructGetData( $tRECT, "Bottom" ) )
          DllStructSetData( $tRECT, "Bottom", DllStructGetData( $tRECT, "Top"    ) + $iRowHeight )
          DllCall( $User32dll, "int", "DrawText", "hwnd", $hDC, "str", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRECT, "int", $DT_LEFT )
        EndIf
      Next
  EndSwitch

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

;RGB to BGR or BGR to RGB
Func ColorConvert($iColor)
  Return BitOR(BitAND($iColor, 0x00FF00), BitShift(BitAND($iColor, 0x0000FF), -16), BitShift(BitAND($iColor, 0xFF0000), 16))
EndFunc

 

Edited by LarsJ
Link to comment
Share on other sites

Wow! now I finally did what I was looking for, a lot of thanks!

But i'm having one more trouble, when i run the script and click fast on the items the background color of the item don't change, it only changes if I wait a while to click (2s +/-).

 

I solved the problem changing this line:

If $itemState = $ODS_SELECTED or $itemState = 769 Then

Am i'm right?

Edited by x_bennY
Link to comment
Share on other sites

Post 7

What do you mean by the background color? Do you mean the blue highlight color of selected row? Are you running exactly the same code as shown in post 5? And $aRows contains 10 rows and 5 columns?

When I select the 10 rows one by one, by holding the arrow down/up key pressed until the last/first row is reached, the highlight color of selected row changes very fast (instantly).

It's the same when I select/click an item with the mouse. The item is immediately redrawn with the selected blue highlight background color.

I'm using Windows 7, AutoIt 3.3.14.2 and running the code as 32 bit code with F5 in SciTE.

Edited by LarsJ
Link to comment
Share on other sites

Yes, i'm using exacly the same code! I'm using Windows 10 64 bits.

And i'm saying about that blue hightlight color of selected row, it just appear if i wait a time to select it.

I solved the problem changing this line (don't know why):

If $itemState = $ODS_SELECTED Or $itemState = 769 Or $itemState = 257 Then

 

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
 Share

  • Recently Browsing   0 members

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