How to search multiple treeview nodes at a particular depth - vb.net

Please does anyone know how I can search the text of multiple treeview nodes at a particular depth on the click of a button? The treeview nodes are arranged as follows:
I want to prevent the user from entering duplicate grandchild nodes of the same title into the treeview, i.e entering 'Movie 2' a second time should throw up a message that Movie 2 has already been entered; if not, then add the new movie title.
The grandchild node titles are fed into the treeview from a textbox. I am using Visual Basic 2010 Express. Thank you in advance.
The code I am using is:
Private Sub Button11_Click(sender As System.Object, e As System.EventArgs) Handles Button11.Click
'New movie title has been introduced into the study
Dim SelectedNode As TreeNode
SelectedNode = TreeView1.SelectedNode
'To avoid entering duplicate movies title
Dim NewMovieName As String = TextBox1.Text.Trim.ToLower ' The content of that node
Dim parentNode = SelectedNode.Parent ' Get the parent
Dim childNodes As TreeNodeCollection = parentNode.Nodes ' Get all the children
Dim WeHaveDuplicate As Boolean = False ' We use this to flag if a duplicate is found. Initially set to false.
For Each tempNode As TreeNode In childNodes
'Test that we have the same name but not referring to the same node
If tempNode.Text.Trim.ToLower = NewMovieName And tempNode IsNot parentNode Then WeHaveDuplicate = True
Next
If WeHaveDuplicate = True Then
'Send message to user
MsgBox(TextBox1.Text & " as a parameter has already been considered.", vbOKOnly)
Exit Sub
Else
parentNode.Nodes.Add(TextBox1.Text)
TreeView1.ExpandAll()
End If
Exit Sub
End Sub
All help would be greatly appreciated. Thank you.

Here is a little snippet that I use frequently. It will find a node by it's text. It will also highlight and expand the node found.
Notice it is recursive, so it will search to the bottom of the supplied node collection (param). If this supplied collection is the root node, then it will search the whole tree.
I usually apply a unique string to the node.tag property. If you adjust the function to look for that, you can have duplicate text displaying while still having a unique string to look for...
''' <summary>
''' Find and Expand Node in Tree View
''' </summary>
Private Function FindNode(ByVal SearchText As String, ByVal NodesToSearch As TreeNodeCollection, ByVal TreeToSearch As TreeView) As TreeNode
Dim ReturnNode As TreeNode = Nothing
Try
For Each Node As TreeNode In NodesToSearch
If String.Compare(Node.Text, SearchText, True) = 0 Then
TreeToSearch.SelectedNode = Node
Node.Expand()
ReturnNode = Node
Exit For
End If
If ReturnNode Is Nothing Then ReturnNode = FindNode(SearchText, Node.Nodes, TreeToSearch)
Next
Catch ex As Exception
Throw
End Try
Return ReturnNode
End Function
Edited:
Per your recent comment,
You might try using it like this...
WeHaveDuplicate = (FindNode("Movie 2", TreeView1.Nodes, TreeView1) Is Nothing)
If WeHaveDuplicate = True Then
'message user of dupe
Else
'add movie
End If

Related

VB.NET 2019 How to populate a Tag Property on a dynamic ToolstripmenuItem

I am struggling to populate a tag property on a dynamically created sub menu which I have created. I have a text file that contains a number of radio station names, BBC1, BBC2, BBC3 for example, as well as the associated stream addresses for said stations. I am able to pull in the names and apply it to the submenu. They appear fine. I can click on the submenus, and the sender() variable confirms the station names correctly. My problem is that I cannot get the Tag property for each sub menu/radio station, to store the stream address. The code gets the radio title cleans the code and inserts it into the RadioStreamsToolStripMenuItem.DropDownItems.Add(station_name) and all is fine. The code then gets the Stream address and inserts it here RadioStreamsToolStripMenuItem.Tag.ToString(). I can tell that it is wrong, but how do I go about ensuring the correct stream goes into the correct radio Tag property. Im very new to this so please be gentle, its just a little hobby.
'======================================================================
'Aquires Radio name and creates a dropdown sub menu within RadioStreams
'======================================================================
Do While (Not line Is Nothing)
If line.StartsWith("#") Then
station_name = Replace(line, "#", "")
Dim x = RadioStreamsToolStripMenuItem.DropDownItems.Add(station_name)
AddHandler x.Click, AddressOf ToolMenuItem_Click
'===================================================
'Aquires Radio Stream to add to the Tag property for each new station
'===================================================
ElseIf line.StartsWith("#") Then
Dim station_stream As String = Replace(line, "#", "")
'The following just checks if the data has gone into the correct place
For Each Myitem As ToolStripItem In RadioStreamsToolStripMenuItem.DropDownItems
If Myitem.Text = station_name Then
MsgBox("MyItems " & Myitem.ToString())
Me.Tag = station_stream
End If
Next
You could use a Dictionary to store the stations a stream addresses. Dictionary lookups are very fast.
I assumed from your code that your text file looks like this
#BBC1
#BBC1streamAddress
#BBC2
#BBC2streamAddress
#BBC3
#BBC3streamAddress
I looped through the lines of the file assigning the trimmed keys and values to the dictionary. Then looped through the dictionary filling the menu. When the user clicks a menu item we get the text of the item and search the dictionary for the correct stream address.
Private RadioDictionary As New Dictionary(Of String, String)
Private Sub OPCode()
Dim lines = File.ReadAllLines("Radio.txt")
For i = 0 To lines.Length - 1 Step 2
RadioDictionary.Add(lines(i).Trim("#"c), lines(i + 1).Trim("#"c))
Next
For Each item In RadioDictionary
Dim x = RadioStreamsToolStripMenuItem.DropDownItems.Add(item.Key)
AddHandler x.Click, AddressOf ToolMenuItem_Click
Next
End Sub
Private Sub ToolMenuItem_Click(sender As Object, e As EventArgs) Handles ToolMenuItem.Click
Dim station = DirectCast(sender, ToolStripDropDownItem).Text
Dim stream = RadioDictionary(station)
End Sub

Visual Basic: reference label control from variable derived from checkbox control

I'm writing a simple Windows Form app in VB using VS Community 2017.
I have 64 checkboxes with 64 associated labels, named chk1 / lbl1 up to chk64 / lbl64. When a checkbox is checked, I want to extract a character from a string and show the answer in the label: e.g. if chk12 is checked, I want lbl12 to be enabled and the text to display the 12th character of the string.
To save writing 64 separate handlers I'm trying to do it in one. I can extract the checked number (e.g. 12) OK and write it to a string, but when I try to manipulate the label control I get an 'Object reference not set to an instance of an object' error.
The code I've come up with so far (largely from searching in here) is:
Private Sub CheckedChanged(sender As Object, e As EventArgs) _
Handles chk1.CheckedChanged, chk2.CheckedChanged 'etc. to 64
' wanted behaviour
'If chk1.Checked Then
' lbl1.Enabled = True
' lbl1.Text = GetChar(userString, 1)
'End If
'If chk2Checked Then
' lbl2.Enabled = True
' lbl2.Text = GetChar(userString, 2)
'End If
' etc. (to 64)
Dim resultsLabel As String
Dim userCheckedBox As Integer
userCheckedBox = CInt(DirectCast(sender, CheckBox).Text)
resultsLabel = "lbl" & DirectCast(sender, CheckBox).Text
Me.Controls(resultsLabel).Enabled = True
Me.Controls(resultsLabel).Text = GetChar(userString, userCheckedBox)
End Sub
I'd be very grateful if someone can nudge me over the line with this. Many thanks!
There is the old trick to use the Tag property of your checkboxes.
You can set the Tag property to the matching label name at design time using the WinForms Designer. So in the chk1.Tag property you will have the "lbl1" string assigned and so on for all the 64 checkboxes.
At this point your code in the event handler is simply
Dim chk = DirectCast(sender, CheckBox)
if chk IsNot Nothing Then
Me.Controls(chk.Tag.ToString()).Enabled = True
Me.Controls(chk.Tag.ToString()).Text = GetChar(userString, CInt(chk.Text))
End If

Checkedlistbox: uncheck items and re-run action upon new selection

I have a checkedlistbox1 which is filled through a search function with a folderbrowserdialog. Once I check one item (=XML file) it fills a listbox according to certain nodes by calling a separate class. This works fine.
What I want it to do next is when I select another item in checkedlistbox1 it unchecks the previously checked item and again runs the separate class to display the nodes of the newly selected item.
My code is a blur of tries according to other searches I've made. Please take note of what I want it to do, this is not like I have my code now because I don't want it to throw an error when I select another item. I just want it to de-select the previous and perform the action again on the newly selected item.
I hope someone can help me out on this one.
code:
Try
Dim checkLstBox As CheckedListBox = CType(sender, CheckedListBox)
Dim targetNum As Integer = 1
If e.NewValue = CheckState.Checked AndAlso checkLstBox.CheckedItems.Count + 1 > targetNum Then
Call ClsMessageBoxes.CheckedListbox1_maxcheck_Form2()
e.NewValue = CheckState.Unchecked
For i As Integer = 0 To f5.CheckedListBox1.Items.Count - 1
f5.CheckedListBox1.SetItemChecked(i, False)
Next 'This part at least throws an error if I select a new item in checkedlistbox1 and de-selects the previous item'
Else
'this part does not work'
f5.ListBoxDestPlate.Items.Clear()
f5.CheckedListlistbox2.SelectedItems.Clear()
'this part is meant to select an item in another checkbox according to certain tekst in the filename'
Dim i As Integer
If ClsSharedProperties2.filePath2.Contains("Text1") Then
i = 1
f5.Checkedlistbox2.SetItemChecked(i, True)
Call ClsScan.scanning2()
ElseIf ClsSharedProperties2.filePath2.Contains("Text2") Then
i = 2
f5.Checkedlistbox2.SetItemChecked(i, True)
Call ClsScan.scanning2()
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message & vbCrLf & "Stack Trace: " & vbCrLf & ex.StackTrace)
End Try
Your code is a bit messy with references to other forms I'm guessing (f5? ClsScan?).
In general, this code will work with the checked item and uncheck any existing items:
Private Sub clb_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles clb.ItemCheck
If e.NewValue = CheckState.Checked Then
For Each i As Integer In clb.CheckedIndices
clb.SetItemChecked(i, False)
Next
MessageBox.Show("Checked " & clb.Items(e.Index).ToString)
End If
End Sub
The MessageBox line would be replaced with you passing the item reference to whatever function or method you need to do your filtering.
One thing to note regarding the ItemCheck event is that the item in the collection isn't actually checked yet. That is why you would have to rely on the e.Index value.

Set a treeview to specific location

I have a folder in my C: drive that I want to access as soon as my form loads. I don't want to scroll through opening each node everytime. I'm wanting to use a treeview as I know how to use a lot of the features in these and it will suit my purpose.
I shall give you an example of what i am wanting using a basic file structure:
C:\Users\user\Documents\Visual Studio 2010\Projects
This would take me a number of nodes to gain access to if I was to to it through the entire treeview. I want my treeview to to start with, so the main node to be
Projects
How would I go about doing this?
Here is an example which assumes the name of the node is the full path of the folder:
Protected Overrides Sub OnLoad(e As EventArgs)
Dim name As String = "c:\users\blairg\documents\visual studio 2010\projects"
Dim testNode As New TreeNode("Projects")
testNode.Name = name
TreeView1.Nodes.Add(testNode)
Dim node() As TreeNode = TreeView1.Nodes.Find(name, True)
If node.Count = 1 Then
TreeView1.SelectedNode = node(0)
End If
MyBase.OnLoad(e)
End Sub
I am sure that the answer above would work. However i managed to sort it out by doing:
Dim backupfolder As String = netpath & "\MANUFPC BACKUP PROCESS\" & site & "\" & factory & "\" & line & "\" & pc
Dim mRootNode As New TreeNode
mRootNode.Text = pc
mRootNode.Tag = backupfolder
mRootNode.Nodes.Add("*DUMMY*")
'adds plus icon to allow extension
backupFolderDirectory.Nodes.Add(mRootNode)
then the two other functions:
Private Sub TreeView1_BeforeCollapse(ByVal sender As Object, ByVal e As TreeViewCancelEventArgs) Handles backupFolderDirectory.BeforeCollapse
' clear the node that is being collapsed
e.Node.Nodes.Clear()
' add a dummy TreeNode to the node being collapsed so it is expandable
e.Node.Nodes.Add("*DUMMY*")
End Sub
Private Sub TreeView1_BeforeExpand(ByVal sender As Object, ByVal e As TreeViewCancelEventArgs) Handles backupFolderDirectory.BeforeExpand
' clear the expanding node so we can re-populate it, or else we end up with duplicate nodes
e.Node.Nodes.Clear()
' get the directory representing this node
Dim mNodeDirectory As DirectoryInfo
mNodeDirectory = New DirectoryInfo(e.Node.Tag.ToString)
' add each subdirectory from the file system to the expanding node as a child node
Try
For Each mDirectory As DirectoryInfo In mNodeDirectory.GetDirectories
' declare a child TreeNode for the next subdirectory
Dim mDirectoryNode As New TreeNode
Dim mystring(1) As String
mystring(0) = mDirectory.FullName
mystring(1) = "directory"
' store the full path to this directory in the child TreeNode's Tag property
mDirectoryNode.Tag = mystring(0)
' set the child TreeNodes's display text
mDirectoryNode.Text = mDirectory.Name
' add a dummy TreeNode to this child TreeNode to make it expandable
mDirectoryNode.Nodes.Add("*DUMMY*")
' add this child TreeNode to the expanding TreeNode
e.Node.Nodes.Add(mDirectoryNode)
Next
For Each mFiles As FileInfo In mNodeDirectory.GetFiles
' declare a child TreeNode for the next subdirectory
Dim mFileNode As New TreeNode
Dim mystring(1) As String
mystring(0) = mFiles.FullName
mystring(1) = "file"
' store the full path to this directory in the child TreeNode's Tag property
mFileNode.Tag = mystring(0)
' set the child TreeNodes's display text
mFileNode.Text = mFiles.Name
' add this child TreeNode to the expanding TreeNode
e.Node.Nodes.Add(mFileNode)
Next
Catch ex As IOException
'sets up 2 different exceptions then the last one catches other exceptions that could be made from adding folder/files etc
e.Node.Remove()
MsgBox("Device/Folder not accessible", MsgBoxStyle.OkOnly, "Device not Ready")
Catch exc As NullReferenceException
e.Node.Remove()
MsgBox("Sorry this File/Folder can not be added", MsgBoxStyle.OkOnly, "Sorry")
Catch exce As Exception
e.Node.Remove()
MsgBox("Device/Folder not accessible", MsgBoxStyle.OkOnly, "Device not Ready")
End Try
End Sub

How to change the data source of a ComboBox?

I have two comboboxes. The data sorce of combobox1 is a list of string that is fixed. Combobox2's data sorce will be a list of string that depends on the selection of combobox1. This is very similar to a common case as follows: first enter your contry, then depending on your enter, the second combobox will show you the list of universities in the country.
'Change selection of first combobox
Private Sub cbxClient_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbxClient.SelectedIndexChanged
Try
If cbxClient.SelectedIndex <> -1 Then
GetAccount()
End If
Catch
Throw
End Try
End Sub
'Based on selection of first combobox, update the data sorce of second combobox
Private Sub GetAccount()
Try
m_listAccount.Clear()
Dim strClient As String = cbxClient.SelectedItem.ToString
For i As Integer = 0 To m_listDS.Count - 1
If m_listDS(i).Client.Tostring = strClient Then
m_ds = m_listDS(i)
Exit For
End If
Next
If Not m_ds Is Nothing Then
For Each row As DataRow In m_ds.Tables("Account").Rows
m_listAccount.Add(row("account").ToString)
Next
End If
cbxAccount.DataSource = m_listAccount
Catch ex As Exception
End Try
End Sub
My problem is that though m_listAccount updates (has the correct information), the choices shown in cbxAccount are not updated according to m_listAccount (has the wrong information). I do not understand why this happens.
Note: Assume that old string in m_listAccount is {"old1"} (the list only has 1 string) and after updating, the string in m_listAccount is {"new"}. Through break points, I get the following:
cbxAccount.DataSorce={"new"}
cbxAccount.SelectedItem={"old1"}
In the form, cbxAccount shows "old1" string.
Try this,
cbxAccount.DataSource = Nothing
cbxAccount.DataSource = m_listAccount
If you are just updating the same list object, the datasource might not automatically update, but should if the pointer changes to the datasource.
Or you could try,
m_listAccount.AcceptChanges()
If m_listAccount is a DataTable
Doesn't look like you're actually calling the databind method, i.e.
cbxAccount.DataBind()
Try putting that just after you set the data source.