Search code examples
excelvb.nettreeviewvsto

Sort data from Text File into tree structure


I have data in a delimited text file containing list of items and their relationship with each other, as shown below

Data set text file

where each Item Id is distinct and parent wbs column shows the relationship with parent items.

i am able to import the above data to excel, but unable to figure out how to structure them in tree format in excel and also make sure that under each node the items are sorted again by their sequence numbers

For i = 3 To LastRow
            Dim found As Boolean = False
             For k = 2 To wbssht.UsedRange.Rows.Count

                If wbssht.Cells(k, 1).value = sht.Cells(i, 3).value Then
                    wbssht.Rows(k + 1).insert
                    wbssht.Cells(k + 1, 1).value = sht.Cells(i, 1).value
                    wbssht.Cells(k + 1, 3).value = wbssht.Cells(k, 3).value + 2
                    wbssht.Cells(k + 1, 2).value = Space(wbssht.Cells(k + 1, 3).value) & sht.Cells(i, 2).value
                    wbssht.Cells(k + 1, 4).value = sht.Cells(k, 3).value 'parentwbs
                    wbssht.Cells(k + 1, 5).value = sht.Cells(k, 4).value 'sequence
                    found = True
                    Exit For
                End If
            Next
            If found = False Then
                wbssht.Cells(wbssht.UsedRange.Rows.Count + 1, 1).value = sht.Cells(i, 1).value
                wbssht.Cells(wbssht.UsedRange.Rows.Count, 2).value = sht.Cells(i, 2).value
                wbssht.Cells(wbssht.UsedRange.Rows.Count, 3).value = 0
                wbssht.Cells(wbssht.UsedRange.Rows.Count, 4).value = sht.Cells(i, 3).value
                wbssht.Cells(wbssht.UsedRange.Rows.Count, 5).value = sht.Cells(i, 4).value
            End If

        Next

I am trying to achieve an indented output structure as image below in excel.

Expected output

I know its probably really simple, but may need some different approach.


Solution

  • Try this code:

    Private Sub TreeView1_KeyUp(KeyCode As Integer, ByVal Shift As Integer)
        
        'Checking what button is pressed.
        Select Case KeyCode
            
            'If F5 is pressed.
            Case Is = 116
                
                'Declarations.
                Dim RngList As Range
                Dim RngTarget As Range
                Dim DblNameOffset As Double
                Dim DblParentOffset As Double
                Dim DblSequenceOffset As Double
                Dim StrMarker As String
                Dim NodNode As Node
                Dim DblRow As Double
                Dim DblCounter01 As Double
                Dim DblItemMax
                Dim ObjTreeView As Object
                
                'Setting RngList as the first value in the Item ID column.
                Set RngList = Sheets("Sheet1").Range("A2")
                
                'Setting ObjTreeView.
                Set ObjTreeView = ActiveSheet.Shapes("TreeView1")
                
                'Setting StrMarker as a value that won't be in any Item ID value nor in any Sequence value.
                StrMarker = " | "
                
                'Setting the variables as offests of each column of data from the Item ID column.
                DblNameOffset = 1
                DblParentOffset = 2
                DblSequenceOffset = 3
                
                'Changing RngList to cover the whole Item ID list.
                Set RngList = RngList.Parent.Range(RngList, RngList.End(xlDown))
                
                'Setting DblItemMax as the count of Item IDs.
                DblItemMax = Excel.WorksheetFunction.CountA(RngList)
                
                'Checking that RngList does not contain any non unique value, non numeric value, blank cell.
                For Each RngTarget In RngList
                    Select Case True
                        Case Is = (Excel.WorksheetFunction.CountIf(RngList, RngTarget.Value) > 1)
                            MsgBox "Non unique item ID found. The treeview will not be updated.", vbCritical + vbOKOnly, "Invalid item ID: " & RngTarget.Value
                            Exit Sub
                        Case Is = (RngTarget.Value = "")
                            MsgBox "Blank ID found. The treeview will not be updated.", vbCritical + vbOKOnly, "Invalid item ID in cell " & RngTarget.Address(False, False)
                            Exit Sub
                        Case Is = (IsNumeric(RngTarget.Value) = False)
                            MsgBox "Non numeric item ID found. The treeview will not be updated.", vbCritical + vbOKOnly, "Invalid item ID: " & RngTarget.Value
                            Exit Sub
                    End Select
                Next
                
                'Clearing ObjTreeView of any previous nodes.
                ObjTreeView.OLEFormat.Object.Object.Nodes.Clear
                
                'Covering each Item ID from the smalles to the greatest.
                For DblCounter01 = 1 To DblItemMax
                    
                    'Setting DblRow as the number of row in RngList that contains the given Item ID.
                    With Excel.WorksheetFunction
                        DblRow = .Match(.Small(RngList, DblCounter01), RngList, 0)
                    End With
                    
                    'Setting RngTarget as the cell that contains the given Item ID.
                    Set RngTarget = RngList.Cells(DblRow, 1)
                    
                    'Checking if the given parent name exist in RngList.
                    If Excel.WorksheetFunction.CountIf(RngList, RngTarget.Offset(0, DblParentOffset).Value) = 0 Then
                        'If it doesn't exist, the new node is added with no parent node.
                        ActiveSheet.Shapes("TreeView1").OLEFormat.Object.Object.Nodes.Add , , "K" & RngTarget.Value, RngTarget.Offset(0, DblSequenceOffset) & StrMarker & RngTarget.Offset(0, DblNameOffset)
                    Else
                        'If it exists, the new node is added under its parent node.
                        ActiveSheet.Shapes("TreeView1").OLEFormat.Object.Object.Nodes.Add "K" & RngTarget.Offset(0, DblParentOffset), tvwChild, "K" & RngTarget.Value, RngTarget.Offset(0, DblSequenceOffset) & StrMarker & RngTarget.Offset(0, DblNameOffset)
                    End If
                Next
                
                'Sorting each node (they were added with the Sequence value at the beginning of it text).
                For Each NodNode In ActiveSheet.Shapes("TreeView1").OLEFormat.Object.Object.Nodes
                    NodNode.Sorted = True
                Next
                
                'Cutting out the sequence value from the text of each node using the properly placed StrMarker.
                For Each NodNode In ActiveSheet.Shapes("TreeView1").OLEFormat.Object.Object.Nodes
                    NodNode.Text = Split(NodNode.Text, StrMarker)(1)
                Next
                
        End Select
        
    End Sub
    

    It's a private sub that will activate when you press F5 while the treeview is selected. Therefore you'll have to place it in the module of the sheet where the treeview is located. It assumes that your treeview is named TreeView1. It also assumes that your list is placed in the cells A1 of a sheet named Sheet1; that means that in cell A1 of Sheet1 you'll find the header "Item ID" while in cell A2 you'll find the first ID. Note that you can have the list in one sheet and the treeview in another one. Anyway you can edit the code itself accordingly to your needs (perhaps you can change the list address, the treeview name or the key to be pressed to activate it). Other paramaters can also be costumized.

    The code checks for any empty, non numeric non unique Item ID and if it finds any of them it terminates itself.

    The list can be sorted in any order. The code should work anyway. In your data sample the first item (27521) has a parent name (18133) with no match in the Item ID column. In case like this, the code create a node with no parent node. In any case it is assumed that any Item ID has a father with a lower Item ID.