Jump to content

Implementing Virtual TreeViews


LarsJ
 Share

Recommended Posts

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 comment
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 comment
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 comment
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 comment
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 comment
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 comment
Share on other sites

Real virtual treeviews
In first part of Virtual treeviews, only the number of child items for a given node/parent was specified. When only the number of child items is specified, these items must appear in a continuous and uninterrupted row of child items. And only one item can be a node/parent, namely the last one in the row.

To work around this limitation, it's necessary to specify child item lists instead of just the number of child items. For each node/parent a list is needed that for each child item indicates the row number in the source file where information about this child item can be found.

Thus the source file must also contain information about these child item lists.

All code is located in the folder Examples\Part 2\1) Real Virtual TreeViews.
 

Simple example  (1) Simple\)
Show the two treeviews by double-clicking TVSourceDisplayPlain.au3 and TVSourceDisplayVirtual.au3.

This source file is sufficient for a plain or semi-virtual treeview (3 levels, 5 items, Plain.txt

00|Item0
01|Item1
02|Item2
01|Item3
00|Item4

In a plain treeview texts are stored in the item structures. In a semi-virtual treeview texts are virtual and stored only in the source file. Therefore, a semi-virtual treeview is faster to create than a plain treeview. For both types, the treeview structure is created when the treeview is created.

In a virtual treeview, more information is needed (3 levels, 5 items, Virtual.txt

00|       |       7| |Item0
01|      0|       1| |Item1
02|      1|        | |Item2
01|      0|        | |Item3
00|       |        | |Item4

Level 0 parents and items = 2
0:1|3|
4:

Level 1 parents = 1

0|5
0:7|2
1:11|1

A virtual treeview means a true virtual treeview where all child items and texts are virtual. Information about child items and texts is stored in the source file. Only root items (first level items) are created when the treeview is created. Because the number of root items is usually low, it's very fast to create a virtual treeview.

The actual treeview item information at top of the source file is a |-separated list with 5 fields: 

1 Treeview item level
2 Row number in source file containing parent item
3 Row number in source file containing child item list
4 Indication of expanded parent
5 Treeview item text

The information at bottom of the source file is divided into several sections. There is a section for "Level 0 parents and items", "Level 1 parents", "Level 2 parents", etc. The very last section is a summary section.

Level 0 parents and items = 2
The child item list for Item0 (at level 0 = first level = root level) is found in row 7 (zero-based) in the source file. Data in row 7 is 0:1|3|. 0 is the row number where the list is used. 1|3| is the child item list itself. Item0 has 2 direct childs in rows 1 and 3. Data in row 8 is 4:. Item4 in row 4 is a root item without any childs.

Level 1 parents = 1
Since the current treeview has 3 levels (level 0, 1 and 2 zero-based), a node/parent at level 1 (second last level) can only have ordinary child items (ie. no nodes/parents). Row 1 in the source file contains a level 1 node/parent. Because the child items are ordinary child items, it's enough to indicate the number of child items. The node/parent in row 1 has 1 child item. This number is stated directly in field 3 instead of the row number for the child item list. Since no child item lists are needed for level 1 (second last level) nodes/parents, the section named "Level 1 parents = 1" is empty.

Level 2 parents
Because there are only 3 levels, there are no node/parent items at level 2 (zero-based). There are only child items. No child lists for "Level 2 parents".

Summary section
The lines at bottom of the source file is a summary section. 0|5 means that the actual treeview item information starts in row 0 and includes 5 rows. 0:7|2 means that child item lists for level 0 starts in row 7 and includes 2 rows. 1:11|1 means that child item lists for level 1 starts in row 11 and includes 1 row. But as mentioned above, it's not necessary to specify child item lists for second last level. The last line in the source file 1:11|1 is instead used to calculate the total number of levels. The number of levels is the number before : plus 1. In this case 2 levels (1+1=2 (zero-based)).
 

TVSourceFile.au3 (Includes\TVSourceFile.au3)
TVSourceFile.au3 is a so far incomplete version of a UDF to handle treeview source files. This is the function list at top of the file: 

; Functions
; ---------------------------------
; TVSourceCreatePlain()              Create random plain source file for non-virtual treeview
; TVSourceCreatePlainDbg()           Create random plain source file for non-virtual treeview. Debug version.
; TVSourceCreatePlainEx()            Based on recursive function with support for 100 levels
; TVSourceCreatePlainExDbg()         Based on recursive function with support for 100 levels. Debug version.
; TVSourceCreatePlainExConst()       Create constant plain source file for non-virtual treeview. Debug version. Constant source file.
; TVSourceCreatePlainExConstCount()  Count number of treeview items generated by TVSourceCreatePlainExConst() above
; TVSourceCreateVirtual()            Create random source file for virtual treeview
; TVSourceCreateVirtualDbg()         Create random source file for virtual treeview. Debug version. Contains code comments. <<<<<<<<<<
;
; TVSourceConvertPlainToVirtual()    Convert plain source file for non-virtual treeview to source file for virtual treeview
; TVSourceConvertVirtualToPlain()    Convert source file for virtual treeview to plain source file for non-virtual treeview
;
; TVSourceDisplayPlain()             Display non-virtual treeview from plain source file
; TVSourceDisplayVirtual()           Display virtual treeview from source file

The functions TVSourceCreatePlain(), TVSourceCreatePlainDbg(), TVSourceCreateVirtual() and TVSourceCreateVirtualDbg() are implemented through loops that can generate source files and thus treeviews with up to 10 levels.

The Ex-functions TVSourceCreatePlainEx(), TVSourceCreatePlainExDbg() and TVSourceCreatePlainExConst() are based on recursive functions that can generate source files with up to 100 levels.

The Dbg- and Const-functions contain debug information in the item texts in the form of an indication of whether an item is a node/parent or a plain item as well as row index in source file and treeview. The other functions generate random item texts.

All functions except TVSourceCreatePlainExConst() generate random treeview structures with a random number of items. True virtual treeviews are limited to 10,000,000 items.

TVSourceCreatePlainExConst() generates a constant treeview in such a way that the same input parameters will always result in the same treeview.

TVSourceCreatePlainExConstCount() calculates the number of items in these treeviews.

TVSourceConvertPlainToVirtual() and TVSourceConvertVirtualToPlain() convert back and forth between plain and virtual treeviews. Due to a complicated structure of a source file for a virtual treeview, it's difficult to update the treeview by editing the source file. The idea is to first convert the virtual file to a plain file. Then update the plain source file. And finally convert back to a virtual file.

TVSourceDisplayPlain() and TVSourceDisplayVirtual() display treeviews in a GUI.
 

Function examples (2) Sources\)
2) Sources\ contains examples of the use of each function. Run the examples in SciTE with F5. Treeview source files are stored in the Sources\ top folder. These are the examples for TVSourceCreatePlainExConst() and TVSourceCreatePlainExConstCount().

4) TVSourceCreatePlainExConst.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 "..\Includes\TVSourceFile.au3"

Global $sSources = "..\..\..\..\Sources\"

; With many levels and thus many treeview items, it can take some time to generate the source file. It may also
; take a long time to create the entire structure for a non-virtual treeview before it can be displayed in the GUI.
ConsoleWrite( "TVSourceCreatePlainExConst( 6, 4, 4, ...) Creating..." & @CRLF )
If TVSourceCreatePlainExConst( 6, 4, 4, $sSources & "6 levels, 26,071 items, Plain, debug, const.txt" ) Then
  ConsoleWrite( "TVSourceCreatePlainExConst( 6, 4, 4, ...) Displaying..." & @CRLF )
  If Not TVSourceDisplayPlain( $sSources & "6 levels, 26,071 items, Plain, debug, const.txt" ) Then _
    ConsoleWrite( "TVSourceDisplayPlain: @error = " & @error & @CRLF )
Else
  ConsoleWrite( "TVSourceCreatePlainExConst: @error = " & @error & @CRLF )
EndIf

5) TVSourceCreatePlainExConstCount.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 "..\Includes\TVSourceFile.au3"

Example()

Func Example()
  Local $iItems0, $iCount
  For $iLevels = 1 To 10
    For $iMultiplier = 2 To 10
      For $iItems = 1 To 10
        $iItems0 = $iItems * $iMultiplier
        If Not TVSourceCreatePlainExConstCount( $iLevels, $iItems0, $iMultiplier, $iCount ) Then
          ConsoleWrite( "TVSourceCreatePlainExConstCount: @error = " & @error & " " & _
            StringFormat( "%3i, %3i, %3i", $iLevels, $iItems0, $iMultiplier ) & @CRLF )
          ExitLoop
        EndIf
        ConsoleWrite( "$iLevels, $iItems0, $iMultiplier, $iCount = " & _
          StringFormat( "%3i, %3i, %3i, %8i", $iLevels, $iItems0, $iMultiplier, $iCount ) & @CRLF )
      Next
    Next
  Next
EndFunc

This is the last part of the output: 

$iLevels, $iItems0, $iMultiplier, $iCount =   7,   2,   2,     6787
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   4,   2,    13574
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   6,   2,    20361
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   8,   2,    27148
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  10,   2,    33935
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  12,   2,    40722
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  14,   2,    47509
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  16,   2,    54296
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  18,   2,    61083
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  20,   2,    67870
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   3,   3,    52145
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   6,   3,   104290
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   9,   3,   156435
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  12,   3,   208580
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  15,   3,   260725
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  18,   3,   312870
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  21,   3,   365015
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  24,   3,   417160
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  27,   3,   469305
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  30,   3,   521450
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   4,   4,   227671
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   8,   4,   455342
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  12,   4,   683013
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  16,   4,   910684
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  20,   4,  1138355
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  24,   4,  1366026
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  28,   4,  1593697
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  32,   4,  1821368
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  36,   4,  2049039
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  40,   4,  2276710
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   5,   5,   741169
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  10,   5,  1482338
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  15,   5,  2223507
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  20,   5,  2964676
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  25,   5,  3705845
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  30,   5,  4447014
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  35,   5,  5188183
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  40,   5,  5929352
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  45,   5,  6670521
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  50,   5,  7411690
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   6,   6,  1999691
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  12,   6,  3999382
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  18,   6,  5999073
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  24,   6,  7998764
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  30,   6,  9998455
TVSourceCreatePlainExConstCount: @error = 2   7,  36,   6
$iLevels, $iItems0, $iMultiplier, $iCount =   7,   7,   7,  4724257
$iLevels, $iItems0, $iMultiplier, $iCount =   7,  14,   7,  9448514
TVSourceCreatePlainExConstCount: @error = 2   7,  21,   7
TVSourceCreatePlainExConstCount: @error = 2   7,   8,   8
TVSourceCreatePlainExConstCount: @error = 2   7,   9,   9
TVSourceCreatePlainExConstCount: @error = 2   7,  10,  10
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   2,   2,    52147
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   4,   2,   104294
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   6,   2,   156441
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   8,   2,   208588
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  10,   2,   260735
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  12,   2,   312882
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  14,   2,   365029
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  16,   2,   417176
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  18,   2,   469323
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  20,   2,   521470
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   3,   3,   455345
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   6,   3,   910690
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   9,   3,  1366035
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  12,   3,  1821380
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  15,   3,  2276725
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  18,   3,  2732070
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  21,   3,  3187415
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  24,   3,  3642760
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  27,   3,  4098105
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  30,   3,  4553450
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   4,   4,  2223511
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   8,   4,  4447022
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  12,   4,  6670533
$iLevels, $iItems0, $iMultiplier, $iCount =   8,  16,   4,  8894044
TVSourceCreatePlainExConstCount: @error = 2   8,  20,   4
$iLevels, $iItems0, $iMultiplier, $iCount =   8,   5,   5,  7998769
TVSourceCreatePlainExConstCount: @error = 2   8,  10,   5
TVSourceCreatePlainExConstCount: @error = 2   8,   6,   6
TVSourceCreatePlainExConstCount: @error = 2   8,   7,   7
TVSourceCreatePlainExConstCount: @error = 2   8,   8,   8
TVSourceCreatePlainExConstCount: @error = 2   8,   9,   9
TVSourceCreatePlainExConstCount: @error = 2   8,  10,  10
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   2,   2,   455347
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   4,   2,   910694
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   6,   2,  1366041
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   8,   2,  1821388
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  10,   2,  2276735
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  12,   2,  2732082
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  14,   2,  3187429
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  16,   2,  3642776
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  18,   2,  4098123
$iLevels, $iItems0, $iMultiplier, $iCount =   9,  20,   2,  4553470
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   3,   3,  4447025
$iLevels, $iItems0, $iMultiplier, $iCount =   9,   6,   3,  8894050
TVSourceCreatePlainExConstCount: @error = 2   9,   9,   3
TVSourceCreatePlainExConstCount: @error = 2   9,   4,   4
TVSourceCreatePlainExConstCount: @error = 2   9,   5,   5
TVSourceCreatePlainExConstCount: @error = 2   9,   6,   6
TVSourceCreatePlainExConstCount: @error = 2   9,   7,   7
TVSourceCreatePlainExConstCount: @error = 2   9,   8,   8
TVSourceCreatePlainExConstCount: @error = 2   9,   9,   9
TVSourceCreatePlainExConstCount: @error = 2   9,  10,  10
$iLevels, $iItems0, $iMultiplier, $iCount =  10,   2,   2,  4447027
$iLevels, $iItems0, $iMultiplier, $iCount =  10,   4,   2,  8894054
TVSourceCreatePlainExConstCount: @error = 2  10,   6,   2
TVSourceCreatePlainExConstCount: @error = 2  10,   3,   3
TVSourceCreatePlainExConstCount: @error = 2  10,   4,   4
TVSourceCreatePlainExConstCount: @error = 2  10,   5,   5
TVSourceCreatePlainExConstCount: @error = 2  10,   6,   6
TVSourceCreatePlainExConstCount: @error = 2  10,   7,   7
TVSourceCreatePlainExConstCount: @error = 2  10,   8,   8
TVSourceCreatePlainExConstCount: @error = 2  10,   9,   9
TVSourceCreatePlainExConstCount: @error = 2  10,  10,  10

@error = 2 means that more than 10,000,000 items are generated.

New 7z-file at bottom of first post.

Edited by LarsJ
Link to comment
Share on other sites

Compiled VB.NET code
Since the source files for virtual treeviews are large files, it's advantageous and necessary to handle the files with compiled code. This post is about compiled VB.NET code.

All code is located in the folder Examples\Part 2\2) Compiled VB.NET Code.
 

Using VB.NET code
Compiled VB.NET methods can be executed in AutoIt scripts through the DotNetAll.au3 UDF. Here the VB.NET code is loaded with the function TVSourceCreateVbNetCodeObject():

; Create $oVbNetCode object
; Executes treeview source file functions as compiled VB.NET code
;
; Error code in @error                             Return value
;     1 -> Failed to locate TVSourceFile.vb            Success -> 1 or $oVbNetCode
;     2 -> Failed to create $oVbNetCode object         Failure -> 0
;
Func TVSourceCreateVbNetCodeObject( _
  $sVbNetCodePath = "" ) ; Relative path to VB.NET source code file
                         ; VB.NET source code is saved in Includes\TVSourceFile.vb
                         ; The path to set is the relative path from the running script to this file

  Local Static $bVbNetCodeObject = 0, $oNetCode, $oVbNetCode
  If Not $sVbNetCodePath And $bVbNetCodeObject Then Return $oVbNetCode

  ; Locate TVSourceFile.vb
  If Not FileExists( $sVbNetCodePath ) Then Return SetError( 1, 0, 0 )

  ; Create $oVbNetCode object
  $oNetCode = DotNet_LoadVBcode( FileRead( $sVbNetCodePath ), "System.dll" )
  $oVbNetCode = DotNet_CreateObject( $oNetCode, "TVSourceFile" )
  If Not IsObj( $oVbNetCode ) Then Return SetError( 2, 0, 0 )
  $bVbNetCodeObject = 1
  Return 1
EndFunc

The first function call is an initialization, where the path to the VB.NET code is specified as a parameter, and where $oVbNetCode is created and stored as a local static variable in the function. A lot of AutoIt code is executed to create the $oVbNetCode object. Therefore, it's performance-optimizing to create the object only once during an initialization.

The $oVbNetCode object is created at the top of a script this way:

If Not TVSourceCreateVbNetCodeObject( "..\Includes\TVSourceFile.vb" ) Then
  ConsoleWrite( "TVSourceCreateVbNetCodeObject: @error = " & @error & @CRLF )
  Exit 1
EndIf

..\Includes\TVSourceFile.vb contains the VB.NET code. In subsequent function calls without a path parameter, the stored $oVbNetCode object is returned.
 

TVSourceFile.au3 (Includes\TVSourceFile.au3)
This is the updated function list at top of file:

; Functions
; -----------------------------------
; TVSourceCreatePlain()                Create random plain source file for non-virtual treeview
; TVSourceCreatePlainDbg()             Create random plain source file for non-virtual treeview. Debug version.
; TVSourceCreatePlainEx()              Based on recursive function with support for 100 levels
; TVSourceCreatePlainExVb()            Based on recursive function with support for 100 levels. VB.NET code.
; TVSourceCreatePlainExDbg()           Based on recursive function with support for 100 levels. Debug version.
; TVSourceCreatePlainExDbgVb()         Based on recursive function with support for 100 levels. Debug version. VB.NET code.
; TVSourceCreatePlainExConst()         Create constant plain source file for non-virtual treeview. Debug version. Constant source file.
; TVSourceCreatePlainExConstVb()       Create constant plain source file for non-virtual treeview. Debug version. Constant source file. VB.NET code.
; TVSourceCreatePlainExConstCount()    Count number of treeview items generated by TVSourceCreatePlainExConst/Vb() above
; TVSourceCreatePlainExConstCountVb()  Count number of treeview items generated by TVSourceCreatePlainExConst/Vb() above. VB.NET code.
; TVSourceCreateVirtual()              Create random source file for virtual treeview
; TVSourceCreateVirtualDbg()           Create random source file for virtual treeview. Debug version. Contains code comments. <<<<<<<<<<
;
; TVSourceConvertPlainToVirtual()      Convert plain source file for non-virtual treeview to source file for virtual treeview
; TVSourceConvertPlainToVirtualVb()    Convert plain source file for non-virtual treeview to source file for virtual treeview. VB.NET code.
; TVSourceConvertVirtualToPlain()      Convert source file for virtual treeview to plain source file for non-virtual treeview
;
; TVSourceDisplayPlain()               Display non-virtual treeview from plain source file
; TVSourceDisplayVirtual()             Display virtual treeview from source file


TVSourceFile.vb (Includes\TVSourceFile.vb)
TVSourceFile.vb contains all the VB.NET code. You may take a closer look at the code yourself if you are interested. These are a few general considerations.

When the VB.NET code is executed, a syntax check comparable to Au3Check is performed. If the AutoIt script is executed in SciTE, the syntax errors are displayed in the console.

A runtime error will usually appear like this in the console:

TVSourceFile.au3 (1323) : ==> The requested action with this object has failed:
Local $iRet = $oVbNetCode.TVSourceConvertPlainToVirtualVb( $iLevels, $sTVPlainSrc, $sTVVirtualSrc )
Local $iRet = $oVbNetCode^ ERROR

Several VB.NET features are used in the code, e.g. Files, Arrays, Lists, Dictionary objects, Recursive functions and Regular expressions.

Also note that AutoIt code can be translated relatively directly and easily into VB.NET code.
 

Examples (Examples\5) TVSourceConvertPlainToVirtualVb.au3)
The Examples\ folder contains an example for each of the 5 functions implemented in VB.NET code. Run the examples in SciTE with F5. Treeview source files are stored in the Sources\ top folder.

This is a closer look at example 5) TVSourceConvertPlainToVirtualVb.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 "..\Includes\TVSourceFile.au3"

If Not TVSourceCreateVbNetCodeObject( "..\Includes\TVSourceFile.vb" ) Then
  ConsoleWrite( "TVSourceCreateVbNetCodeObject: @error = " & @error & @CRLF )
  Exit 1
EndIf

Example( 6, 16, 4,  104284 ) ;   104,284 treeview items
;Example( 5, 81, 9, 1012329 ) ; 1,012,329 treeview items

Func Example( $iLevels, $iItems0, $iMultiplier, $iCount )
  Local $sSources = "..\..\..\..\Sources\" & $iLevels & " levels, " & $iCount & " items, "
  ConsoleWrite( "TVSourceCreatePlainExConstVb( " & $sSources & "Plain, debug.txt" & " ) Creating..." & @CRLF )
  If TVSourceCreatePlainExConstVb( $iLevels, $iItems0, $iMultiplier, $sSources & "Plain, debug.txt" ) Then
    ConsoleWrite( "TVSourceConvertPlainToVirtualVb( " & $sSources & "Plain, debug.txt" & " ) Converting..." & @CRLF )
    If TVSourceConvertPlainToVirtualVb( $iLevels, $sSources & "Plain, debug.txt", $sSources & "Virtual, debug.txt" ) Then
      ConsoleWrite( "TVSourceDisplayVirtual( " & $sSources & "Virtual, debug.txt" & " ) Displaying..." & @CRLF )
      If Not TVSourceDisplayVirtual( $sSources & "Virtual, debug.txt" ) Then _
        Return ConsoleWrite( "TVSourceDisplayVirtual: @error = " & @error & @CRLF )
    Else
      Return ConsoleWrite( "TVSourceConvertPlainToVirtualVb: @error = " & @error & @CRLF )
    EndIf
  Else
    Return ConsoleWrite( "TVSourceCreatePlainExConstVb: @error = " & @error & @CRLF )
  EndIf
EndFunc

Notice how TVSourceCreateVbNetCodeObject() is initialized in the top part of the code.

TVSourceConvertPlainToVirtualVb() is called in the middle of the function:

; Convert plain source file for non-virtual treeview to source file for virtual treeview. VB.NET code.
;
; Error code in @error                        Return value
;     1 -> Invalid $iLevels value                 Success -> 1
;     2 -> Plain source file doesn't exist        Failure -> 0
;     3 -> Invalid or empty $aSource array
;     4 -> Invalid source file item format
;     5 -> Too many treeview elements
;     6 -> Couldn't get $oVbNetCode
;
; Errors 3 - 5 comes from TVSourceConvertPlainToVirtualVb() in TVSourceFile.vb
;
Func TVSourceConvertPlainToVirtualVb( _
  $iLevels, _      ; Treeview levels, 2 <= $iLevels <= 100
  $sTVPlainSrc, _  ; Name of plain treeview input source file
  $sTVVirtualSrc ) ; Name of virtual treeview output source file

  If Not ( 2 <= $iLevels And $iLevels <= 100 ) Then Return SetError( 1, 0, 0 )

  If Not FileExists( $sTVPlainSrc ) Then Return SetError( 2, 0, 0 )

  ; Get $oVbNetCode object to execute function as compiled VB.NET code
  Local $oVbNetCode = TVSourceCreateVbNetCodeObject()
  If Not IsObj( $oVbNetCode ) Then Return SetError( 6, 0, 0 )

  ; Execute TVSourceConvertPlainToVirtualVb() as compiled VB.NET code
  Local $iRet = $oVbNetCode.TVSourceConvertPlainToVirtualVb( $iLevels, $sTVPlainSrc, $sTVVirtualSrc )
  If $iRet < 0 Then Return SetError( -$iRet+2, 0, 0 )
  Return 1
EndFunc

Notice again how TVSourceCreateVbNetCodeObject() is now called without a parameter. Also note that the name of the VB.NET method is the same as the name of the AutoIt function.

New 7z-file at bottom of first post.

Edited by LarsJ
Link to comment
Share on other sites

Virtual TreeView functions
These are, for now, the last functions directly concentrated on handling TreeView source files. Some of the functions are also implemented in VB.NET code.

All code located in  Examples\Part 2\3) Virtual TreeView Functions. Run the examples in SciTE with F5.
 

TVSourceFile.au3 (Includes\TVSourceFile.au3)
This is the updated function list at top of file: 

; Functions
; -------------------------------------
; TVSourceCreateVbNetCodeObject()        Create $oVbNetCode object to execute treeview source file functions as compiled VB.NET code
;
; TVSourceCreatePlain()                  Create random plain source file for non-virtual treeview
; TVSourceCreatePlainDbg()               Create random plain source file for non-virtual treeview. Debug version.
; TVSourceCreatePlainEx()                Based on recursive function with support for 100 levels
; TVSourceCreatePlainExVb()              Based on recursive function with support for 100 levels. VB.NET code.
; TVSourceCreatePlainExDbg()             Based on recursive function with support for 100 levels. Debug version.
; TVSourceCreatePlainExDbgVb()           Based on recursive function with support for 100 levels. Debug version. VB.NET code.
; TVSourceCreatePlainExConst()           Create constant plain source file for non-virtual treeview. Debug version. Constant source file.
; TVSourceCreatePlainExConstVb()         Create constant plain source file for non-virtual treeview. Debug version. Constant source file. VB.NET code.
; TVSourceCreatePlainExConstCount()      Count number of treeview items generated by TVSourceCreatePlainExConst/Vb() above
; TVSourceCreatePlainExConstCountVb()    Count number of treeview items generated by TVSourceCreatePlainExConst/Vb() above. VB.NET code.
; TVSourceCreateVirtual()                Create random source file for virtual treeview
; TVSourceCreateVirtualDbg()             Create random source file for virtual treeview. Debug version. Contains code comments. <<<<<<<<<<
;
; TVSourceCountPlainLevels()             Count number of levels in plain treeview source file
; TVSourceCountPlainLevelsVb()           Count number of levels in plain treeview source file. VB.NET code.
; TVSourceCountSubtreeItems()            Count number of subtree items in plain/virtual treeview source file
; TVSourceCountSubtreeItemsVb()          Count number of subtree items in plain/virtual treeview source file. VB.NET code.
;
; TVSourceConvertPlainToVirtual()        Convert plain source file for non-virtual treeview to source file for virtual treeview
; TVSourceConvertPlainToVirtualVb()      Convert plain source file for non-virtual treeview to source file for virtual treeview. VB.NET code.
; TVSourceConvertVirtualToPlain()        Convert source file for virtual treeview to plain source file for non-virtual treeview
;
; TVSourceExtractSubtreeFromPlain()      Extract plain subtree source file from plain treeview source file
; TVSourceExtractSubtreeFromPlainVb()    Extract plain subtree source file from plain treeview source file. VB.NET code.
; TVSourceExtractSubtreeFromVirtual()    Extract plain subtree source file from virtual treeview source file
; TVSourceExtractSubtreeFromVirtualVb()  Extract plain subtree source file from virtual treeview source file. VB.NET code.
;
; TVSourceGetParentsFromVirtual()        Get parents from root to item in true virtual source file
;
; TVSourceDisplayPlain()                 Display non-virtual treeview from plain source file
; TVSourceDisplayVirtual()               Display virtual treeview from source file

The new functions are the Count and Extract functions and TVSourceGetParentsFromVirtual().

Optionally collapse the first root node in the CountSubtreeItems and plain Extract examples. Then item 3432 becomes visible, which is the start item in these examples.

New 7z-file at bottom of first post.

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