Jump to content

TreeViews and Duplicate children (and grandchildren)


Recommended Posts

Hi guys,

Sorry for such a trivial post, but this is really doing my head in.  I've spent all day trying to figure this out and the last 3 hours searching everything on this forum that mentions "treeview".

I'm trying to build a Treeview based on variable input data.  The problem is that some of the data is duplicated, and I can't figure out how to avoid creating duplicate children in the treeview.

Code:

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

$a = GUICreate("My GUI with treeview", 700, 315)

Global $treeview = GUICtrlCreateTreeView(6, 6, 338, 303, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS), $WS_EX_CLIENTEDGE)
Build("Apps|WordPerfect")
Build("Apps|Word")
Build("Apps|Outlook")
Build("Apps|Outlook|Addins")
Build("Training|Outlook")

Func Build($input)
    Local $child
    Local $items = StringSplit($input, "|")
    For $i = 1 To $items[0]
        If ($i > 1) Then ; child item
            $child = _GUICtrlTreeView_AddChild($treeview, $child, $items[$i])
        Else ; root item
            $child = _GUICtrlTreeView_Add($treeview, 1, $items[$i])
        EndIf
    Next
EndFunc   ;==>Build


; Output that I want it to look like, based on the above "input".
Global $treeview2 = GUICtrlCreateTreeView(350, 6, 338, 303, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS), $WS_EX_CLIENTEDGE)
$apps = _GUICtrlTreeView_Add($treeview2, "", "Apps")
_GUICtrlTreeView_AddChild($treeview2, $apps, "WordPerfect")
_GUICtrlTreeView_AddChild($treeview2, $apps, "Word")
$outlook = _GUICtrlTreeView_AddChild($treeview2, $apps, "Outlook")
_GUICtrlTreeView_AddChild($treeview2, $outlook, "Addins")
$training = _GUICtrlTreeView_Add($treeview2, "", "Training")
_GUICtrlTreeView_AddChild($treeview2, $training, "Outlook")

GUISetState(@SW_SHOW)

While (1)
    $nmsg = GUIGetMsg()
    Switch $nmsg
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

 

The Treeview on the left is what my "build" code creates.  The Treeview on the right is what I want it to look like, based on the string input I provided to the Build function.

In other words, the Treeview shouldn't just blindly create a new element, but should check for an existing one of the same name.  If it exists, then it drills down into that sub-tree, and then repeats the same process.

I've investigated the FindItem function, but that doesn't help since it searches the entire treeview, and it might find an item under the wrong heading (ie. Outlook might be found under Apps, when I'm trying to search under Training).  I looked into FindItemEx, but that gets complicated with the 4th Input (ie. Apps/Outlook would exist, but the FindEx would fail on Apps/Outlook/Addins).

Every solution I've tried of listing the children of a node to see if one already exists fails.  I think a further complication that I'm struggling with is the difference between a ControlID and a Handle.

The other thing I'll mention is that I can't use variables for each "group" like in the working example, as the input will eventually contain hundreds of lines with many duplicates.

It's doing my head in, no matter what ideas I try, it doesn't produce the right output.  I'm sure it can't be that complicated, but I'm just at the end of my patience with this.  Please, if anyone can provide some guidance, it'll stop me from screaming in frustration.

Link to comment
Share on other sites

  • Moderators

kinch,

If you are prepared to add a couple of lines to create the first level items then this code works: ;)

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

; Array to hold item titles and ControlIDs
Global $aTreeViewItem[2][2] = [[1, 0], ["", 0]]

$hGUI = GUICreate("Test", 500, 500)

$aTreeViewItem[1][1] = GUICtrlCreateTreeView(10, 10, 480, 380)

GUISetState()

_Build("Apps") ; Base level item - needed
_Build("Apps|WordPerfect")
_Build("Apps|Word")
_Build("Apps|Outlook")
_Build("Apps|Outlook") ; Duplicate for test
_Build("Apps|Outlook|Addins")
_Build("Training") ; Base level item - needed
_Build("Training|Outlook")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _Build($sString)

    If _ArraySearch($aTreeViewItem, $sString, 1) <> -1 Then
        ; Duplicate
        Return
    EndIf

    ; Split path
    $aSplit = StringSplit($sString, "|")
    
    ; Look for path removing one leyer each time
    For $i = $aSplit[0] To 1 Step -1
        ; This is the path we are looking for this time
        $sPattern = ""
        ; Build it up one layer at a time
        For $j = 1 To $i - 1
            $sPattern &= $aSplit[$j] & "|"
        Next
        ; Remove final "|"
        $sPattern = StringTrimRight($sPattern, 1)
        ; Is this path already in the treeview?
        $iIndex = _ArraySearch($aTreeViewItem, $sPattern, 1)
        ; If so then exit loop
        If $iIndex Then ExitLoop
    Next

    ; Add an element to the array
    $aTreeViewItem[0][0] += 1
    ReDim $aTreeViewItem[$aTreeViewItem[0][0] + 1][2]
    ; Save the path
    $aTreeViewItem[$aTreeViewItem[0][0]][0] = $sString
    ; Get the title to add
    $sTitle = $sString
    If $sPattern Then $sTitle = StringReplace($sString, $sPattern & "|", "")

    If $iIndex = -1 Then
        ; Add at root if nothing found
        $aTreeViewItem[$aTreeViewItem[0][0]][1] = GUICtrlCreateTreeViewItem($sTitle, $aTreeViewItem[1][1])
    Else
        ; Or add to found path
        $aTreeViewItem[$aTreeViewItem[0][0]][1] = GUICtrlCreateTreeViewItem($sTitle, $aTreeViewItem[$iIndex][1])
    EndIf

    ; Expand any newly create branches
    _GUICtrlTreeView_Expand($aTreeViewItem[1][1])

EndFunc   ;==>_Build
I hope the comments are clear enough - please ask if not. :)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

  • Moderators

kinch,

After a bit more thought, here is a version which works with your original strings: :)

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

Global $aTreeViewItem[2][2] = [[1, 0], ["", 0]]

$hGUI = GUICreate("Test", 500, 500)

$aTreeViewItem[1][1] = GUICtrlCreateTreeView(10, 10, 480, 380)

GUISetState()

_Build("Apps|WordPerfect")
_Build("Apps|Word")
_Build("Apps|Outlook")
_Build("Apps|Outlook") ; Duplicate for check
_Build("Apps|Outlook|Addins")
_Build("Training|Outlook")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _Build($sString)

    If _ArraySearch($aTreeViewItem, $sString, 1) <> -1 Then
        ; Duplicate
        Return
    EndIf

    ; Split path
    $aSplit = StringSplit($sString, "|")

    ; Check if base level exists and create if not
    If _ArraySearch($aTreeViewItem, $aSplit[1], 1) = -1 Then
        ; Add an element to the array
        $aTreeViewItem[0][0] += 1
        ReDim $aTreeViewItem[$aTreeViewItem[0][0] + 1][2]
        ; Save the name
        $aTreeViewItem[$aTreeViewItem[0][0]][0] = $aSplit[1]
        ; Add at root
        $aTreeViewItem[$aTreeViewItem[0][0]][1] = GUICtrlCreateTreeViewItem($aSplit[1], $aTreeViewItem[1][1])
    EndIf

    ; Look for string removing one leyer each time
    For $i = $aSplit[0] To 1 Step -1
        ; This is the path we are looking for this time
        $sPattern = ""
        ; Build it up one layer at a time
        For $j = 1 To $i - 1
            $sPattern &= $aSplit[$j] & "|"
        Next
        ; Remove final "|"
        $sPattern = StringTrimRight($sPattern, 1)
        ; Is this path already in the treeview?
        $iIndex = _ArraySearch($aTreeViewItem, $sPattern, 1)
        ; If so then exit loop
        If $iIndex Then ExitLoop
    Next

    ; Add an element to the array
    $aTreeViewItem[0][0] += 1
    ReDim $aTreeViewItem[$aTreeViewItem[0][0] + 1][2]
    ; Save the path
    $aTreeViewItem[$aTreeViewItem[0][0]][0] = $sString
    ; Get the title to add
    $sTitle = $sString
    If $sPattern Then $sTitle = StringReplace($sString, $sPattern & "|", "")

    If $iIndex = -1 Then
        ; Add at root if nothing found
        $aTreeViewItem[$aTreeViewItem[0][0]][1] = GUICtrlCreateTreeViewItem($sTitle, $aTreeViewItem[1][1])
    Else
        ; Or add to found path
        $aTreeViewItem[$aTreeViewItem[0][0]][1] = GUICtrlCreateTreeViewItem($sTitle, $aTreeViewItem[$iIndex][1])
    EndIf

    _GUICtrlTreeView_Expand($aTreeViewItem[1][1])

EndFunc   ;==>_Build
I must admit to being quite pleased with it. :graduated:

Again, please ask if you have any questions. :)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

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