Jump to content

Implementing Virtual TreeViews


Recommended Posts

Posted (edited)

Multi-line treeview items
Multi-line treeview items are supported through the iIntegral field in the TVITEM structure and custom draw code. The iIntegral value handles multi-line items in the treeview structure. Custom draw code draws the entire item rectangle, the dotted focus rectangle and the individual multi-line texts.

Multi-line treeview items are supported by all treeviews and not just semi-virtual or virtual treeviews.

To add a multi-line item with 2, 3 or 4 lines, simply set the value of iIntegral to 2, 3 or 4.

Both the width and height of a rectangle that can accommodate all the lines must be calculated in the custom draw code. The height is just 2, 3 or 4 times the height of a single-line item. The width must be set to the width of the widest text string in pixels of the 2, 3, or 4 substrings. The width of a text string in pixels is calculated using the GetTextExtentPoint32W() function. The left position of the rectangle is calculated with _GUICtrlTreeView_GetIndent() and the item level. You'll also need to consider that the width of the entire treeview control may not be large enough to accommodate the longest text strings.

Data source for multi-line items (Multiline.txt)

0|0
0|1
0|2
1|3
1|This|Line 2
1|is
2|6
2|a
2|very
2|nice|Line 2|Line 3
3|10
3|TreeView
0|, (comma)
1|13
1|indeed.
0|15

Code in a semi-virtual treeview to create and draw multi-line items (1) Multiline Items.au3)

; Create TreeView structure
Func CreateTreeView( $aItems )
  ; TreeView item information
  Local $aItem, $iItem

  ; TreeView level information
  Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0
  ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level

  ; TreeView insert structure
  Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert )
  DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST )
  DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT+$TVIF_INTEGRAL )
  DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK

  ; Add TreeView root
  $aItem = StringSplit( $aItems[0], "|", 2 )
  $iItem = UBound( $aItem )
  $aLevels[$iLevel][1] = NULL
  DllStructSetData( $tInsert, "Param", 0 )
  DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
  DllStructSetData( $tInsert, "Integral", $iItem > 2 ? $iItem - 1 : 1 )
  $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )

  ; Add TreeView items
  For $i = 1 To UBound( $aItems ) - 1
    $aItem = StringSplit( $aItems[$i], "|", 2 )
    $iItem = UBound( $aItem )
    $iLevel = $aItem[0]
    If $iLevel <> $iLevelPrev Then
      $aLevels[$iLevel][1] = $iLevel > $iLevelPrev ? $aLevels[$iLevelPrev][0] _ ; A child of the previous level
                                                   : GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level
      $iLevelPrev = $iLevel
    EndIf
    DllStructSetData( $tInsert, "Param", $i )
    DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] )
    DllStructSetData( $tInsert, "Integral", $iItem > 2 ? $iItem - 1 : 1 )
    $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert )
  Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level
EndFunc

Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
    Case $hTreeView
      Switch DllStructGetData( $tNMHDR, "Code" )
        Case $TVN_GETDISPINFOW ; Display TreeView item text
          Local Static $tBuffer = DllStructCreate( "wchar Text[50]" ), $pBuffer = DllStructGetPtr( $tBuffer )
          Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")], "|", 2 )[1]
          DllStructSetData( $tBuffer, "Text", $sText )
          DllStructSetData( $tDispInfo, "Text", $pBuffer )
          DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 )

        Case $NM_CUSTOMDRAW
          Local $tCustomDraw = DllStructCreate( $tagNMTVCUSTOMDRAW, $lParam )
          Switch DllStructGetData( $tCustomDraw, "DrawStage" ) ; The current drawing stage
            Case $CDDS_PREPAINT              ; Before the paint cycle begins
              Return $CDRF_NOTIFYITEMDRAW    ; Notify the parent before painting an item

            Case $CDDS_ITEMPREPAINT          ; Before painting an item
              If UBound( StringSplit( $aItems[DllStructGetData($tCustomDraw,"ItemParam")], "|", 2 ) ) > 2 Then _
                Return $CDRF_NOTIFYPOSTPAINT ; Notify the parent after painting an item

            ; Draw multiline item texts
            Case $CDDS_ITEMPOSTPAINT         ; After painting an item
              Local Static $iTVIndent = _GUICtrlTreeView_GetIndent( $hTreeView ), $tSize = DllStructCreate( $tagSIZE ), $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE ), $hBrushWhite = _WinAPI_CreateSolidBrush( 0xFFFFFF )
              Local $hDC = DllStructGetData( $tCustomDraw, "HDC" ), $tRect = DllStructCreate( $tagRECT, DllStructGetPtr( $tCustomDraw, "Left" ) ), $aItem = StringSplit( $aItems[DllStructGetData($tCustomDraw,"ItemParam")], "|", 2 ), $iItem = UBound( $aItem ), $iMaxTextLen = 0, $iLeft = $iTVIndent * ( $aItem[0] + 1 ) + 3, $iRectWidth = DllStructGetData( $tCustomDraw, "Right" ) - $iLeft, $iTop = DllStructGetData( $tRect, "Top" ), $iItemState = DllStructGetData( $tCustomDraw, "ItemState" )
              ; Longest text in pixels of all item texts
              For $i = 1 To $iItem - 1
                DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $aItem[$i], "int", StringLen( $aItem[$i] ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
                If DllStructGetData( $tSize, "X" ) + 1 > $iMaxTextLen Then $iMaxTextLen = DllStructGetData( $tSize, "X" ) + 1
              Next
              ; Rectangle that includes all item texts
              If $iMaxTextLen + 4 > $iRectWidth Then _
                $iMaxTextLen = $iRectWidth - 4
              DllStructSetData( $tRect, "Left", $iLeft )
              DllStructSetData( $tRect, "Right", $iLeft + $iMaxTextLen + 4 )
              DllStructSetData( $tRect, "Bottom", $iTop + 18 * ( $iItem - 1 ) )
              DllCall( "gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT ) ; _WinAPI_SetBkMode()
              Switch BitAND( $iItemState, $CDIS_SELECTED + $CDIS_FOCUS )
                Case $CDIS_SELECTED + $CDIS_FOCUS
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushHighLight ) ; _WinAPI_FillRect()
                  DllCall( "user32.dll", "bool", "DrawFocusRect", "handle", $hDC, "struct*", $tRect ) ; _WinAPI_DrawFocusRect()
                  DllCall( "gdi32.dll", "INT", "SetTextColor", "handle", $hDC, "INT", 0xFFFFFF ) ; _WinAPI_SetTextColor()
                Case $CDIS_SELECTED
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushButtonFace ) ; _WinAPI_FillRect()
                Case $CDIS_FOCUS
                Case Else
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushWhite ) ; _WinAPI_FillRect()
              EndSwitch
              ; Left start position for item texts
              DllStructSetData( $tRect, "Left", $iLeft + 2 )
              ; Draw all item texts
              For $i = 1 To UBound( $aItem ) - 1
                DllStructSetData( $tRect, "Top", $iTop + 18 * ( $i - 1 ) + 1 )
                DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $aItem[$i], "int", -1, "struct*", $tRect, "uint", $DT_LEFT+$DT_WORD_ELLIPSIS ) ; _WinAPI_DrawText()
              Next
          EndSwitch
      EndSwitch
  EndSwitch
  #forceref $hWnd, $iMsg, $wParam
EndFunc

B21dTYw.png

 

Store MaxTextLen in data source
Because the width of the longest text string in pixels ($iMaxTextLen) must be calculated each time a multi-line item needs to be redrawn, it can be an advantage to store this width directly in the data source.

Multiline-2.txt:

0|000|0
0|000|1
0|000|2
1|000|3
1|000|This|Line 2
1|000|is
2|000|6
2|000|a|A somewhat long new line
2|000|very
2|000|nice|A TreeView item with three lines|Line 3
3|000|10
3|000|TreeView
0|000|, (comma)
1|000|13
1|000|indeed.
0|000|15

2) Store MaxTextLen.au3:

; Cleanup
GUIDelete( $hGui )
If $bSaveItems Then _FileWriteFromArray( "Multiline-2.txt", $aItems )

$aItem = StringSplit( $aItems[DllStructGetData($tCustomDraw,"ItemParam")], "|", 2 )
$iMaxTextLen = $aItem[1]+0
; Longest text in pixels of all item texts
If Not $iMaxTextLen Then
  For $i = 2 To $iItem - 1
    DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $aItem[$i], "int", StringLen( $aItem[$i] ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
    If DllStructGetData( $tSize, "X" ) + 1 > $iMaxTextLen Then $iMaxTextLen = DllStructGetData( $tSize, "X" ) + 1
  Next
  $bSaveItems = True
  $iIndex = DllStructGetData( $tCustomDraw, "ItemParam" )
  $aItems[$iIndex] = StringReplace( $aItems[$iIndex], 3, StringFormat( "%03i", $iMaxTextLen ) )
EndIf

ffUF2uX.png

 

Too narrow treeview control
3) Store MaxTextLen.au3 is similar to the example above except that the treeview control is too narrow for the longest text strings.

Hcanxrx.png

 

New 7z-file at bottom of first post. The first set of examples in 1) Virtual TreeView\ has all been updated with some very minor changes.

Edited by LarsJ
Orange header
Link to post
Share on other sites

Excellent post, it makes it easier and faster to create a treeview:thumbsup:. Thanks LarsJ !

If it could add functions such as drag-drop, add-delete-items, edit-Item-text and so on, it's would more perfect

I've been looking for a similar example of a treeview that can dragdrop, edit, and rearrange items freely

Link to post
Share on other sites

A virtual treeview with hundreds of thousands of items will most likely be used to provide some sort of overview of data, while it's less likely that it'll be used to correct a few individual items.

If there's a need to manually correct a few individual items, then it'll be better to create a treeview that contains only those few items that it's about. When it comes to (human) manual correction of data, it's of course only about a very small amount of data. It's simply not possible to manually correct a very large amount of data.

And when it comes to only a small amount of data, there's no problem in using a conventional treeview where all necessary functionality is already implemented.

When it comes to very large amounts of data, then it must be a requirement that data is handled completely automatically.

At least what I'm doing with virtual treeviews here is meant to provide some sort of overview of data. Therefore, it isn't my plan to code functions to handle individual items.

Another problem with updating individual items in a large virtual treeview is that the update must take place in the data source. Data isn't stored in the treeview itself. But to update a large array (e.g. move array elements up/down when a treeview item is deleted/added) in an interpreted language like AutoIt isn't fast. But even worse is that since the array index is stored in the Param field of the TVITEM structure, moving array elements up/down will invalidate many of these Param values. And it'll ruin the whole idea by using a virtual treeview.
 

Item images
Set the iImage and iSelectedImage fields in the TVITEM structure to the value $I_IMAGECALLBACK to use item images in a virtual treeview. And then set the index in the image list of the item image when responding to TVN_GETDISPINFO notifications.

Here, the item image index in the image list is indicated in the second column (Item Images.txt)

0|0|0
0|0|1
0|0|2
1|0|3
1|0|This|Line 2
1|0|is
2|1|6
2|1|a
2|1|very
2|1|nice|Line 2|Line 3
3|1|10
3|2|TreeView
0|2|, (comma)
1|2|13
1|2|indeed.
0|2|15

The same item image is used for both selected and unselected treeview items.

Code to create image list:

; Create small image ImageList
Local $hImageList = _GUIImageList_Create( 16, 16, 5, 1 )
; Add small images to ImageList
_GUIImageList_Add( $hImageList, _WinAPI_CreateSolidBitmap( $hGui, 0xFF0000, 16, 16 ) ) ; Index 0, Red
_GUIImageList_Add( $hImageList, _WinAPI_CreateSolidBitmap( $hGui, 0xFF00FF, 16, 16 ) ) ; Index 1, Magenta
_GUIImageList_Add( $hImageList, _WinAPI_CreateSolidBitmap( $hGui, 0x0000FF, 16, 16 ) ) ; Index 2, Blue

; Add ImageList to TreeView
_GUICtrlTreeView_SetNormalImageList( $hTreeView, $hImageList )

Set Image and SelectedImage fields in the TVITEM structure:

DllStructSetData( $tInsert, "Image", -1 ) ; $I_IMAGECALLBACK
DllStructSetData( $tInsert, "SelectedImage", -1 ) ; $I_IMAGECALLBACK

Respond to TVN_GETDISPINFO notifications:

Case $TVN_GETDISPINFOW ; Display TreeView item text and image
  Local Static $tBuffer = DllStructCreate( "wchar Text[50]" ), $pBuffer = DllStructGetPtr( $tBuffer )
  Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $aItem = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")], "|", 2 )
  DllStructSetData( $tBuffer, "Text", $aItem[2] )
  DllStructSetData( $tDispInfo, "Text", $pBuffer )
  DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $aItem[2] ) + 2 )
  DllStructSetData( $tDispInfo, "Image", $aItem[1] )
  DllStructSetData( $tDispInfo, "SelectedImage", $aItem[1] )

 

Multi-line items
For multi-line items, the rectangle containing all text lines should be shifted to the right to make room for the item image. Ie. the $iLeft value of the rectangle should be increased by the width of the image plus a few pixels:

; $iLeft without item image
$iLeft = $iTVIndent * ( $aItem[0] + 1 ) + 3

; $iLeft with item image
$iLeft = $iTVIndent * ( $aItem[0] + 1 ) + 16 + 3 + 3

H0yZmLS.png

 

New 7z-file at bottom of first post.

Link to post
Share on other sites

Performance tests
The examples in first post have been used for performance tests on the basis of a common source file, 10100.txt, with 10,100 treeview items of which 100 are root items. In addition, the source file 10000.txt with 1 root item and 9,999 direct child items is used to test the creation of direct child items in a true virtual treeview.

The tests are run as 64 bit code on Windows 7 and 10. Results on Windows 7 with AutoIt 3.3.14.2:

1) Conventional TreeView.au3
CreateTreeView(): 1084.16993786727

2) Optimized TreeView.au3
CreateTreeView(): 121.195523739788

3) Semi-Virtual TreeView.au3
CreateTreeView(): 100.011054067689

4) Virtual TreeView.au3
CreateTreeView(): 4.07172012097971

5) Virtual TreeView 9999 Child Items.au3
Create 9999 Child Items: 254.070404160541

The true virtual tree view in test 4 is created very fast because there are only 100 root items. On the other hand, the creation of 9,999 direct child items in test 5 is relatively slow.

Results on Windows 10 with AutoIt 3.3.15.3 Beta:

1) Conventional TreeView.au3
CreateTreeView(): 1519.9362

2) Optimized TreeView.au3
CreateTreeView(): 212.4225

3) Semi-Virtual TreeView.au3
CreateTreeView(): 183.8063

4) Virtual TreeView.au3
CreateTreeView(): 18.1912

5) Virtual TreeView 9999 Child Items.au3
Create 9999 Child Items: 455.3943

Note that the tests here without exception are all a lot slower than the tests on Windows 7. Probably because Windows 10 is a more advanced, complex and sophisticated operating system than Windows 7 and therefore runs a lot more code to perform the same task.
 

Structure of a semi-virtual treeview
In 6) Semi-Virtual TreeView Structure.au3, the code line GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") is commented out. This means that only the tree structure is created based on information in the TVITEM structures. The tree structure itself is drawn by default code, but the texts depend on TVN_GETDISPINFO notifications in WM_NOTIFY messages, and are not drawn. In the image below, the first root item is expanded:

ckbd9qn.png

 

New 7z-file at bottom of first post.

Link to post
Share on other sites

Miscellaneous
The second post above is about multi-line treeview items that are supported by all treeviews through the iIntegral field in the TVITEM structure and custom draw code.
 

_GUICtrlTreeView_SetItemHeight()
Setting the iIntegral value is handled by _GUICtrlTreeView_SetItemHeight() in GuiTreeView.au3. But the accompanying example in the help file only shows how the first line of a multi-line text is drawn with default code. It doesn't show how subsequent lines are drawn with custom draw code. And to get a good result, it's also necessary to draw the first line with custom draw code. This is demonstrated in Miscellaneous\1) _GUICtrlTreeView_SetItemHeight.au3:

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

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

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

Global $idTreeView, $hTreeView, $aItems = FileReadToArray( "Multiline.txt" )

Example()

Func Example()
  ; TreeView style
  Local $iStyle = BitOR( $TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS, $TVS_CHECKBOXES ), $ahItem[6]

  ; Create GUI
  GUICreate( "TreeView Set Item Height", 400, 300 )

  ; Create TreeView
  $idTreeView = GUICtrlCreateTreeView( 2, 2, 396, 268, $iStyle, $WS_EX_CLIENTEDGE )
  $hTreeView = GUICtrlGetHandle( $idTreeView )
  For $x = 0 To UBound( $ahItem ) - 1
    $ahItem[$x] = _GUICtrlTreeView_AddChild( $idTreeView, NULL, StringFormat( "[%02d] New Item", $x + 1 ) )
  Next
  _GUICtrlTreeView_SetItemParam( $idTreeView, $ahItem[2], 2 ) ; Index in $aItems
  _GUICtrlTreeView_SetItemHeight( $idTreeView, $ahItem[2], 2 ) ; $iIntegral

  ; Register WM_NOTIFY message handler
  GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

  ; Show GUI
  GUISetState( @SW_SHOW )

  ; Loop until the user exits
  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE
  GUIDelete()
EndFunc

Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
    Case $hTreeView
      Switch DllStructGetData( $tNMHDR, "Code" )
        Case $NM_CUSTOMDRAW
          Local $tCustomDraw = DllStructCreate( $tagNMTVCUSTOMDRAW, $lParam )
          Switch DllStructGetData( $tCustomDraw, "DrawStage" ) ; The current drawing stage
            Case $CDDS_PREPAINT              ; Before the paint cycle begins
              Return $CDRF_NOTIFYITEMDRAW    ; Notify the parent before painting an item

            Case $CDDS_ITEMPREPAINT          ; Before painting an item
              If UBound( StringSplit( $aItems[DllStructGetData($tCustomDraw,"ItemParam")], "|", 2 ) ) > 2 Then _
                Return $CDRF_NOTIFYPOSTPAINT ; Notify the parent after painting an item

            ; Draw multiline item texts
            Case $CDDS_ITEMPOSTPAINT         ; After painting an item
              Local Static $iTVIndent = _GUICtrlTreeView_GetIndent( $hTreeView ), $tSize = DllStructCreate( $tagSIZE ), $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE ), $hBrushWhite = _WinAPI_CreateSolidBrush( 0xFFFFFF )
              Local $hDC = DllStructGetData( $tCustomDraw, "HDC" ), $tRect = DllStructCreate( $tagRECT, DllStructGetPtr( $tCustomDraw, "Left" ) ), $aItem = StringSplit( $aItems[DllStructGetData($tCustomDraw,"ItemParam")], "|", 2 ), $iItem = UBound( $aItem ), $iMaxTextLen = 0, $iLeft = $iTVIndent * ( $aItem[0] + 1 ) + 16, $iRectWidth = DllStructGetData( $tCustomDraw, "Right" ) - $iLeft, $iTop = DllStructGetData( $tRect, "Top" ), $iItemState = DllStructGetData( $tCustomDraw, "ItemState" )
              ; Longest text in pixels of all item texts
              For $i = 1 To $iItem - 1
                DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $aItem[$i], "int", StringLen( $aItem[$i] ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
                If DllStructGetData( $tSize, "X" ) + 1 > $iMaxTextLen Then $iMaxTextLen = DllStructGetData( $tSize, "X" ) + 1
              Next
              ; Rectangle that includes all item texts
              If $iMaxTextLen + 4 > $iRectWidth Then _
                $iMaxTextLen = $iRectWidth - 4
              DllStructSetData( $tRect, "Left", $iLeft )
              DllStructSetData( $tRect, "Right", $iLeft + $iMaxTextLen + 4 )
              DllStructSetData( $tRect, "Bottom", $iTop + 18 * ( $iItem - 1 ) )
              DllCall( "gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT ) ; _WinAPI_SetBkMode()
              Switch BitAND( $iItemState, $CDIS_SELECTED + $CDIS_FOCUS )
                Case $CDIS_SELECTED + $CDIS_FOCUS
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushHighLight ) ; _WinAPI_FillRect()
                  DllCall( "user32.dll", "bool", "DrawFocusRect", "handle", $hDC, "struct*", $tRect ) ; _WinAPI_DrawFocusRect()
                  DllCall( "gdi32.dll", "INT", "SetTextColor", "handle", $hDC, "INT", 0xFFFFFF ) ; _WinAPI_SetTextColor()
                Case $CDIS_SELECTED
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushButtonFace ) ; _WinAPI_FillRect()
                Case $CDIS_FOCUS
                Case Else
                  DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrushWhite ) ; _WinAPI_FillRect()
              EndSwitch
              ; Left start position for item texts
              DllStructSetData( $tRect, "Left", $iLeft + 2 )
              ; Draw all item texts
              For $i = 1 To UBound( $aItem ) - 1
                DllStructSetData( $tRect, "Top", $iTop + 18 * ( $i - 1 ) + 1 )
                DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $aItem[$i], "int", -1, "struct*", $tRect, "uint", $DT_LEFT+$DT_WORD_ELLIPSIS ) ; _WinAPI_DrawText()
              Next
          EndSwitch
      EndSwitch
  EndSwitch
  #forceref $hWnd, $iMsg, $wParam
EndFunc

n4A8j0X.png

Note that in my example, I've removed the $TVS_EDITLABELS style because the Edit control by default only supports editing the first line of a multi-line item. To use $TVS_EDITLABELS with multi-line items, the Edit control should of course support editing of all the lines.

 

New 7z-file at bottom of first post.

Link to post
Share on other sites

UDF versions
The last section planned in this example about virtual treeviews is UDF versions of the code.

The functional requirements in the example were in order of priority:

  • An external data source needed to implement a virtual treeview
  • Virtual treeviews to speed up the creation of large treeviews
  • Multi-line treeview items to avoid overly long text lines
  • Other visual functionality supported by virtual treeviews

An external data source is fundamental to any virtual control. First post is about creating a virtual treeview based on an external data source. Because the Microsoft documentation on virtual treeviews is very brief, the easiest approach is to start with a conventional treeview, then reduced and optimized code where the TVITEM structures are filled in directly, a semi-virtual treeview where only the item texts are virtual but not the tree structure itself, and finally a true virtual treeview where also the tree structure is virtual except for root items.

The second post is about multi-line treeview items that are supported by all treeviews through the iIntegral field in the TVITEM structure and custom draw code. In the post just above, it's demonstrated that multi-line items are also useful in conventional treeviews.

Other visual functionality that can be implemented in a true virtual treeview is limited to what can be handled through default drawing code, TVN_GETDISPINFO notifications and custom draw code. 

Fourth post about Item images shows that images are supported by default drawing code when the index in the image list is set through TVN_GETDISPINFO notifications. Checkboxes are fully supported by default drawing code. However, for multi-line items, the rectangle containing all lines must be shifted to the right to make room for images and checkboxes.

In Custom drawn TreeViews, it's demonstrated how to set text and background color as well as font properties through custom draw code. This example is also valid for virtual treeviews.
 

Features in a UDF
The features that will be implemented in a UDF will be limited to treeviews created solely on the basis of an external data source. Other than that features are likely to be coded for all four types of tree views (conventional, optimized, semi-virtual, and virtual).

The fact that multi-line items work in a conventional treeview is a reasonably good reason to include a few functions for conventional treeviews.

Another reason to include conventional treeviews in a UDF is that all treeview features only work in a conventional treeview. All features also work in a treeview based on optimized code. That type is interesting in a UDF because of the speed.

A semi-virtual treeview is required if the entire treeview structure is to be created in advance so that only item texts and images can be virtual. Therefore, this type should be included in a UDF.

A true virtual treeview is the Formula One version of the treeviews. This is the version the whole example is about. And it's the absolutely essential version of a UDF.

So far, not a single line has been coded in a UDF. So a UDF isn't just around the corner. A UDF will be presented when it's ready.

Link to post
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...