none
Searching TreeView subnodes for duplicate names before inserting a node RRS feed

  • Question

  • I have been trying this for ages and just can't get it.

    I want to create a Treeview from a datatable in the following structure:-

    Level1    Level2    Level3       Level 4

    Network, Wireless, Access Point

    Network, Wireless, Wireless, Access Point

    etc

    Now the problem is that when I search to see if a node exists using 

    If TreeView1.Nodes.Find(l1, True).Length = 0 Then


    Then I get results from the entire tree so as shown above it will fail to add Wireless at level 2 as it exists at level 3, If I set the search to not search all children then it only searches level 1, which is fine for level 1 but what I really need to do is set a search to look for duplicates at the level i'm creating nodes at.

    level1

    Search all level 1 nodes for name and if it exists don't insert

    Level 2

    Search all level 3 nodes for name and if it exists don't insert

    Level 3

    Search all level 3 nodes for name and if it exists don't insert

    I guess I need some way to set the node at which I'm working but I just cant seem to find any way to do this. My existing code is:-

    Dim searchChildren As Boolean = False

    Dim l1 As String = ""
    Dim l2 As String = ""
    Dim l3 As String = ""

    With TreeView1 If TreeView1.Nodes.Find(l1, searchChildren).Length = 0 Then .Nodes("0").Nodes.Add(l1, l1) End If If TreeView1.Nodes.Find(l2, searchChildren).Length = 0 Then .Nodes("0").Nodes(l1).Nodes.Add(l2, l2) End If If TreeView1.Nodes.Find(l3, searchChildren).Length = 0 Then .Nodes("0").Nodes(l1).Nodes(l2).Nodes.Add(l3, l3) End If End With

    I am at a loss any help you could give would be very much appreciated.

    Thanks.


    Saturday, December 21, 2019 10:07 AM

Answers

  • Hi

    Here is a reworked version. I started from scratch and came up with this,

    It sets a bunch of values as nodes in the Treeview, and using Button1, adds a bunch more (placed according to existing (or not) nodes)

    The idea is that a new node will be created if the node path is not found, or, if node path is found then subsequent nodes in the supplied string will be added to that path.

    ' Form1 with default TreeView1
    ' and Button1
    Option Strict On
    Option Explicit On
    Public Class Form1
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
    		Dim master As New List(Of String)
    		master.Add("Network, Wireless, Adaptors")
    		master.Add("Networking, Wired, Adaptors")
    		master.Add("Network, Wired, Switches")
    		master.Add("Network, Wireless, Switches")
    
    		' add the initial set of data
    		MakeNodes(master)
    
    		TreeView1.ExpandAll()
    	End Sub
    	Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    		' create a new list of items and
    		' add them to the Treeview
    		' NOTE: each list of strings will
    		' be incorporated to existing
    		' nodes if any
    		Dim nl As New List(Of String)
    		nl.AddRange({"A, B, C", "Networking, Wired, Bb, CC, DD", "EEE, FFFF, GGGG"})
    		MakeNodes(nl)
    
    		' make new group of nodes
    		MakeNodes({"11, 22, 33"}.ToList)
    
    		' add an item to a given node
    		MakeNodes({"Network, Wireless, Adaptors, 111, 222, 333"}.ToList)
    
    		TreeView1.ExpandAll()
    	End Sub
    
    	Sub MakeNodes(lst As List(Of String))
    		For Each s As String In lst
    			Dim n As TreeNode = Nothing
    			For Each node As String In Split(s, ","c)
    				If n Is Nothing Then
    					With TreeView1
    						If .Nodes.ContainsKey(node) Then
    							n = .Nodes(node)
    						Else
    							n = .Nodes.Add(node, node)
    						End If
    					End With
    				Else
    					If n.Nodes.ContainsKey(node) Then
    						n = n.Nodes(node)
    					Else
    						n = n.Nodes.Add(node, node)
    					End If
    				End If
    			Next
    		Next
    	End Sub
    End Class


    Regards Les, Livingston, Scotland

    • Marked as answer by RogerWhitfield Monday, December 23, 2019 8:50 AM
    Sunday, December 22, 2019 7:23 PM

All replies

  • Hi

    It can be very confusing to decide what you want exactly. There are a few variations. I have code below that will add the 3 items (l1,l2 and l3) to the currently selected node if the first one (l1) does not already exist there. I started with a few default nodes in the tree.

    You would really need to define the exact conditions for each of the l1,l2 and l3 items that would determine if they are added or not.

    Option Strict On
    Option Explicit On
    Public Class Form1
      Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        TreeView1.ExpandAll()
      End Sub
      Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
        Dim searchChildren As Boolean = False
    
        Dim l1 As String = "Node111"
        Dim l2 As String = "Node222"
        Dim l3 As String = "Node333"
    
        With TreeView1
          If TreeView1.SelectedNode.Nodes.Find(l1, searchChildren).Length = 0 Then
            ' add all 3 new nodes
            .SelectedNode.Nodes.Add(l1, l1)
            .SelectedNode.Nodes(l1).Nodes.Add(l2, l2)
            .SelectedNode.Nodes(l1).Nodes(l2).Nodes.Add(l3, l3)
          End If
        End With
        TreeView1.ExpandAll()
      End Sub
    End Class


    Regards Les, Livingston, Scotland

    Saturday, December 21, 2019 2:17 PM
  • Thanks for the help, note sure I quite have it yet but I have written a small test program to try and get to the bottom of the problem.

    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            TreeView1.ExpandAll()   ' expand all branches to view full tree
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            TreeView1.HideSelection = False
            updateNodes("Networking", "Wireless", "Adaptors")
            updateNodes("Networking", "Wired", "Adaptors")
            updateNodes("Networking", "Wired", "Switches")
            updateNodes("Networking", "Wireless", "Switches")
            TreeView1.ExpandAll()   ' expand all branches to view full tree
        End Sub
        Private Sub updateNodes(l1 As String, l2 As String, l3 As String)
            Dim searchChildren As Boolean = True
            Dim myNode() As TreeNode
    
            TreeView1.SelectedNode = TreeView1.Nodes(0) ' select root node before starting
            With TreeView1
                myNode = .Nodes.Find(l1, searchChildren) ' Find l1  
                If myNode.Length > 0 Then
                    .SelectedNode = myNode(0)   ' If node exists select it
                Else
                    .SelectedNode.Nodes.Add(l1, l1)   ' node does not exist so add it
                    myNode = .Nodes.Find(l1, False) ' Find new node 
                    If myNode.Length > 0 Then .SelectedNode = myNode(0)       ' select new node
                End If
                ' level 2
                myNode = .Nodes.Find(l2, searchChildren) ' Find l2  
                If myNode.Length > 0 Then
                    .SelectedNode = myNode(0)       ' If node exists select it
                Else
                    .SelectedNode.Nodes.Add(l2, l2)   ' node does not exist so add it
                    myNode = .Nodes.Find(l2, False) ' Find new node 
                    If myNode.Length > 0 Then .SelectedNode = myNode(0)       ' select new node
                End If
                ' Level 3
                myNode = .Nodes.Find(l3, searchChildren) ' Find l3 
                If myNode.Length > 0 Then
                    .SelectedNode = myNode(0)       ' If node exists select it
                Else
                    .SelectedNode.Nodes.Add(l3, l3)   ' node does not exist so add it
                End If
            End With
        End Sub
    End Class
     

    Although this seems to work it fails whenever there is a duplicate name in another branch of the tree, I have tried turning searchChildren to false but that doesn't help.

    I don't seem to be able to search within a single node. I'm clearly missing something.

    Saturday, December 21, 2019 11:05 PM
  • Hi

    Using your code, with the addition of a line to add the Root node, I get this Tree. Does this look like the Tree you expected?

    The line I added was:

        TreeView1.HideSelection = False
        TreeView1.Nodes.Add("Networking")
        updateNodes("Networking", "Wireless", "Adaptors")
    


    Regards Les, Livingston, Scotland

    Saturday, December 21, 2019 11:28 PM
  • Sorry I forgot to say I had manually created the initial node at design time; but yes, that's the result I get.

    The problem is that I'm trying to create the categories for I.T equipment, so many of the sub categories clash as you can have Networking, Wired, Routers as well as Networking, Wireless, Routers etc.

    When I search using 

     Treeview1.Nodes.Find("SearchString", searchChildren)

    It always seems to start searching from the root node, so when I add say "Networking, Wired, Switches" it sees that "Switches" already exists in "Networking, Wireless, Switches" and therefore as an existing category it doesn't add it.

    What I am trying to get is:-

    Networking

              |-- Wired

              |     |-- Adaptors

              |     |-- Switches

              |-- Wireless

                     |-- Adaptors

                     |-- Switches

    Eventually the tree will be populated from a datatable with hundreds of subcategories, so if I can't get it right with 4 manual entries what chance do I have when I try and throw an entire datatable at it.

    I am finding TreeView one of the most difficult tools to work with, I guess if I can't get it working I will just have to adjust the program to find another solution, it does however seem such a shame to give up on TreeView as it looks so elegant. 


    • Edited by RogerWhitfield Sunday, December 22, 2019 2:07 PM Included extra info
    Sunday, December 22, 2019 2:01 PM
  • Hi

    Here is a reworked version. I started from scratch and came up with this,

    It sets a bunch of values as nodes in the Treeview, and using Button1, adds a bunch more (placed according to existing (or not) nodes)

    The idea is that a new node will be created if the node path is not found, or, if node path is found then subsequent nodes in the supplied string will be added to that path.

    ' Form1 with default TreeView1
    ' and Button1
    Option Strict On
    Option Explicit On
    Public Class Form1
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
    		Dim master As New List(Of String)
    		master.Add("Network, Wireless, Adaptors")
    		master.Add("Networking, Wired, Adaptors")
    		master.Add("Network, Wired, Switches")
    		master.Add("Network, Wireless, Switches")
    
    		' add the initial set of data
    		MakeNodes(master)
    
    		TreeView1.ExpandAll()
    	End Sub
    	Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    		' create a new list of items and
    		' add them to the Treeview
    		' NOTE: each list of strings will
    		' be incorporated to existing
    		' nodes if any
    		Dim nl As New List(Of String)
    		nl.AddRange({"A, B, C", "Networking, Wired, Bb, CC, DD", "EEE, FFFF, GGGG"})
    		MakeNodes(nl)
    
    		' make new group of nodes
    		MakeNodes({"11, 22, 33"}.ToList)
    
    		' add an item to a given node
    		MakeNodes({"Network, Wireless, Adaptors, 111, 222, 333"}.ToList)
    
    		TreeView1.ExpandAll()
    	End Sub
    
    	Sub MakeNodes(lst As List(Of String))
    		For Each s As String In lst
    			Dim n As TreeNode = Nothing
    			For Each node As String In Split(s, ","c)
    				If n Is Nothing Then
    					With TreeView1
    						If .Nodes.ContainsKey(node) Then
    							n = .Nodes(node)
    						Else
    							n = .Nodes.Add(node, node)
    						End If
    					End With
    				Else
    					If n.Nodes.ContainsKey(node) Then
    						n = n.Nodes(node)
    					Else
    						n = n.Nodes.Add(node, node)
    					End If
    				End If
    			Next
    		Next
    	End Sub
    End Class


    Regards Les, Livingston, Scotland

    • Marked as answer by RogerWhitfield Monday, December 23, 2019 8:50 AM
    Sunday, December 22, 2019 7:23 PM
  • Thank you very much for your help in this matter.

    The key to this seems to be using TreeView1.Nodes.ContainsKey(node) instead of TreeView1.Nodes.Find(l1, searchChildren).

    The general principle seems to be to locate a the matching node and set that as the current node and then search again from there, I was struggling with setting the correct node to start each search.

    Thanks again, I now have a point from which I can continue.

    Monday, December 23, 2019 8:55 AM
  • For completeness I am adding the final code after I have added some comments and additional categories.

    ' Form1 with default TreeView1
    ' and Button1
    Option Strict On
    Option Explicit On
    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            Dim master As New List(Of String)
            master.Add("Components, Networking, Wireless, Adaptors")
            master.Add("Components, Networking, Wired, Adaptors")
            master.Add("Components, Networking, Wired, Switches")
            master.Add("Components, Networking, Wireless, Switches")
            master.Add("Components, Networking, Wireless, Router, VDSL")
            master.Add("Components, Networking, Wired, Router, ADSL")
            master.Add("Components, Networking, Wireless, Router, ADSL")
            master.Add("Components, Networking, Wired, Router, VDSL")
            master.Add("Components, Networking, Wired, Router, ADSL, 1")
            master.Add("Components, Networking, Wired, Router, ADSL, 2+")
            master.Add("Components, Memory, DDR3")
            master.Add("Components, Memory, DDR3, DIMM")
            master.Add("Components, Memory, DDR3, SODIMM")
            master.Add("Components, Memory, DDR4")
            master.Add("Components, Memory, DDR4, DIMM")
            master.Add("Components, Memory, DDR4, SODIMM")
            master.Add("Software, System, Microsoft, Windows")
            master.Add("Software, Productivity, Microsoft, Office")
            ' add the initial set of data
            MakeNodes(master)
    
            TreeView1.ExpandAll()
        End Sub
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            ' create a new list of items and
            ' add them to the Treeview
            ' NOTE: each list of strings will
            ' be incorporated to existing
            ' nodes if any
    
            '' make new group of nodes
            MakeNodes({"11, 22, 33"}.ToList)
    
            ' add an item to a given node
            MakeNodes({"Components, Networking, Wireless, Adaptors, 111, 222, 333"}.ToList)
    
            TreeView1.ExpandAll()
        End Sub
    
        Sub MakeNodes(lst As List(Of String))
            For Each s As String In lst                     ' Take each string in list of strings
                Dim n As TreeNode = Nothing                 ' Create a blank TreeNode
                For Each node As String In Split(s, ","c)   ' Loop For Each comma delimited item In String
                    If n Is Nothing Then                    ' n is set later so will always be nothing at first pass
                        With TreeView1
                            If .Nodes.ContainsKey(node) Then    ' Check Treeview1.Nodes for a match
                                n = .Nodes(node)                ' if it matches select that node
                            Else
                                n = .Nodes.Add(node, node)      ' if it doesn't match create new node
                            End If
                        End With
                    Else                                    ' If n is something
                        If n.Nodes.ContainsKey(node) Then   ' Check Treeview1.Nodes for a match
                            n = n.Nodes(node)               ' if it matches select that node
                        Else
                            n = n.Nodes.Add(node, node)     ' if it doesn't match create new node
                        End If
                    End If
                Next
            Next
        End Sub
    End Class

    Hopefully this will help others.

    Monday, December 23, 2019 9:11 AM
  • For completeness I am adding the final code after I have added some comments and additional categories.

    ' Form1 with default TreeView1 ' and Button1 Option Strict On Option Explicit On Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Dim master As New List(Of String) master.Add("Components, Networking, Wireless, Adaptors") master.Add("Components, Networking, Wired, Adaptors") master.Add("Components, Networking, Wired, Switches") master.Add("Components, Networking, Wireless, Switches") master.Add("Components, Networking, Wireless, Router, VDSL") master.Add("Components, Networking, Wired, Router, ADSL") master.Add("Components, Networking, Wireless, Router, ADSL") master.Add("Components, Networking, Wired, Router, VDSL") master.Add("Components, Networking, Wired, Router, ADSL, 1") master.Add("Components, Networking, Wired, Router, ADSL, 2+") master.Add("Components, Memory, DDR3") master.Add("Components, Memory, DDR3, DIMM") master.Add("Components, Memory, DDR3, SODIMM") master.Add("Components, Memory, DDR4") master.Add("Components, Memory, DDR4, DIMM") master.Add("Components, Memory, DDR4, SODIMM") master.Add("Software, System, Microsoft, Windows") master.Add("Software, Productivity, Microsoft, Office") ' add the initial set of data MakeNodes(master) TreeView1.ExpandAll() End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click ' create a new list of items and ' add them to the Treeview ' NOTE: each list of strings will ' be incorporated to existing ' nodes if any '' make new group of nodes MakeNodes({"11, 22, 33"}.ToList) ' add an item to a given node MakeNodes({"Components, Networking, Wireless, Adaptors, 111, 222, 333"}.ToList) TreeView1.ExpandAll() End Sub Sub MakeNodes(lst As List(Of String)) For Each s As String In lst ' Take each string in list of strings Dim n As TreeNode = Nothing ' Create a blank TreeNode For Each node As String In Split(s, ","c) ' Loop For Each comma delimited item In String

            node = Trim(node)

    If n Is Nothing Then ' n is set later so will always be nothing at first pass With TreeView1 If .Nodes.ContainsKey(node) Then ' Check Treeview1.Nodes for a match n = .Nodes(node) ' if it matches select that node Else n = .Nodes.Add(node, node) ' if it doesn't match create new node End If End With Else ' If n is something If n.Nodes.ContainsKey(node) Then ' Check Treeview1.Nodes for a match n = n.Nodes(node) ' if it matches select that node Else n = n.Nodes.Add(node, node) ' if it doesn't match create new node End If End If Next Next End Sub End Class


    Hopefully this will help others.

    Hi

    I have found out why the Nodes.Find method was causing a problem. The method was not at fault, but it was US that caused the issues. We didn't think to strip off leading/trailing spaces, and subsequently had a TreeView that had Nodes with either/both and so when a string was being searched for, which wouldn't have leading/trailing spaces, no matches could be found.

    *

    The answer is simple and straightforward - eliminate unwanted spaces from Node names and text.

    *

    In the code you posted, this could be accomplished by adding the BOLD line.

    For Each node As String In Split(s, ","c)

    node = Trim(node)

    *

    Subsequent Nodes.Find should not cause these issues and work as expected. An example:

    I added a TextBox1, Button2 and a Label1

    Button Click event

      Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    
        Dim nodes() As TreeNode = TreeView1.Nodes.Find(Trim(TextBox1.Text), True)
        If nodes.Length > 0 Then
          Label1.Text = nodes(0).FullPath
          TreeView1.SelectedNode = nodes(0)
          TreeView1.Select()
        Else
          Label1.Text = "Not Found"
        End If
    
      End Sub
    Anyway, I thought that maybe you should take care of those spurious space character possibilities if you are hoping to use the Nodes.Find at any stage.


    Regards Les, Livingston, Scotland


    • Edited by leshay Monday, December 23, 2019 2:54 PM
    Monday, December 23, 2019 2:52 PM