How to remove a Specific Button from Flow Layout Panel - vb.net

I am creating dynamic buttons, each has a unique Tag, each tag is then sent to a list box. i want to be able to select the tag from the list box and click a button to remove the button with that specific tag i tried making this code but no go....
Private Sub cmdRemove_Click(sender As Object, e As EventArgs) Handles cmdRemove.Click
Try
Dim curItem As String = RmvList.SelectedItem.ToString()
MsgBox(curItem)
For Each Button As System.Windows.Forms.Control In Main.FloLay.Controls
If Button.Tag = curItem Then
Main.FloLay.Controls.RemoveAt(CurItem)
End If
Next
Catch ex1 As Exception
End Try
End Sub
How can i Achieve this Feat? Thank you In Advance.

RemoveAt takes a number as parameter that is the index in the collection.
You have to do two things:
Use Remove instead of RemoveAt
Exit the for loop
So the code:
For Each Button As System.Windows.Forms.Control In Main.FloLay.Controls
If Button.Tag = curItem Then
Main.FloLay.Controls.Remove(Button)
Exit For
End If
Next
The first change is obvious, the second change is needed because you alter the enumerator when you remove the button and it would throw an exception if you try to continue the for loop after the deletion.
If you want to delete multiple items you can use the following trick:
For i = Main.FloLay.Controls.Count -1 To 0 Step -1
If Main.FloLay.Controls(i).Tag = curItem Then
Main.FloLay.Controls.RemoveAt(i) 'Here you actually provide the index!
End If
Next
This will work because you don't alter any index yet to come be deleting an item if you move backwards through the collection.

Related

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.

How can I count files in multiple listbox items

The purpose...
How can I read how many files in multiple folders.
So within the program I'm supposed to add and remove folders that I wanna monitor. So I'm adding folders to a listbox. The listbox will eventually contain a few items that are paths like \\server\parent directory\directory
The issue
Now this all works, adding the specified paths as items to the listbox, but now I want to count files in all the folders that are in the listbox and output a number to a textbox.
I've figured out how to do this if I have a textbox that contains a single path;
Dim counter = My.Computer.FileSystem.GetFiles(tbchannel1.Text)
tbCount1.Text = ("" & CStr(counter.Count))
But I can't figure out how to twist this around to work with all the items in a listbox instead.
...And btw, this is going to happen at the press of a button. Eventuelly I'll hook up a timer that button.performclick
Thanks!
This should do it for you
Private Sub CountFilesButton_Click(sender As Object, e As EventArgs) Handles CountFilesButton.Click
Try
Dim fileTotal As Integer
For Each item As String In DirListBox.Items
fileTotal += My.Computer.FileSystem.GetFiles(item.ToString).Count
Next
FileCountLabel.Text = String.Format("File count: {0}", fileTotal.ToString)
Catch ex As Exception
MessageBox.Show(String.Concat("An error occurred ", ex.Message))
End Try
End Sub
It'll be up to you to validate the path exists and handle other errors.

Problems when calling a public sub

I'm facing a deadend When trying to call this sub :
Public Sub backblue(ByVal frm As Form, ByVal boxname As String)
For i = 1 To 3
CType(frm.Controls(boxname & i.ToString()), TextBox).BackColor = Color.LightBlue
Next
End Sub
with button click event :
Private Sub Button1_click and bla bla....
backblue(Me, "txb1_")
End Sub
Can anybody show me a suggestion to fix the code.
It throws "Object Referrence not set to an instance bla bla" error
For information the textbox names are :
txb1_1 , txb1_2 , txb1_3
(these are some of the many textboxes in the form that i want its bakcolor changed)
and these three textboxes are already created through designer, not from execution.
i did check the textboxes names and there's nothing wrong.
the form class is also public.
if they are the only textboxs on said form you can just loop through
For Each box as Textbox In frm.Controls
box.BackColor = Color.LightBlue
Next
This error will occur if you do not declare the Form class to be public.
Also, make sure the textbox names are really correct, although this will probably cause a different error.
If you create the textboxes during execution, make sure they are initialized with New and added to the form's Controls collection.
Try this....
Public Sub backblue(ByVal frm As Form, ByVal prefix As String)
For i = 1 To 3
Dim bxName as String = prefix & i.ToString()
Dim bx as TextBox = CType(frm.Controls(bxName), TextBox)
If bx Is Nothing Then
MsgBox("Unable to find text box " +bxName)
Dim mtch() As Control = frm.Controls.Find(bxName, true)
If mtch.Length> 0 then
bx = mtch(0)
Else
Continue For
End if
End If
Bx.BackColor = Color.LightBlue
Next
End Sub
Although, a better solution would be to either create the textboxes inside a control and pass that control to BackBlue or to create an collection that has the controls and pass that in. Which brings up what is most likely yor problem your control is contained in a sub component and thus is not in the main form control collection
Alternative, you could use either the tag of the control or create a component control that implements IExtenderProvider and add it to the form --all of the above would effectively allow you to define the controls and/how they should be handled at designtime.
It may really seem that the names generated by this loop may not be the names of the original textboxes. My suggestion is before setting this Color property verify that the names generated by this loop are indeed the actual names. Maybe output this in a messagebox:
MessageBox.Show(boxname & i.ToString()) for each loop before you set the property

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.

Recursive search for all folders and subfolders in system

I wrote a Windows Forms script that searched for all non-hidden and non-readonly folders in my system. But the script itself, when run initially, runs for like 5 minutes. Subsequent opens take much less time. I was wondering if there is a logical error to it, so as to why its running so very slow.
Private Function FindSubFolders(ByVal dir As DirectoryInfo, ByVal node As TreeNode) As TreeNode
Dim subnode As New TreeNode
For Each folder As DirectoryInfo In dir.GetDirectories()
If (folder.Attributes And FileAttributes.Hidden) <> FileAttributes.Hidden Then
subnode = node.Nodes.Add(folder.FullName, folder.Name)
subnode = FindSubFolders(folder, subnode)
End If
Next
Return subnode
End Function
Private Sub SetFolders_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Is it possible to load this on 1st (initial) form load???
Try
Dim node As TreeNode
If TreeView1.Nodes.Count < 1 Then
For Each drive As String In Directory.GetLogicalDrives
Directory.GetLogicalDrives()
Dim folders As DirectoryInfo = New DirectoryInfo(drive)
If (folders.Attributes And FileAttributes.ReadOnly) <> FileAttributes.ReadOnly Then
node = TreeView1.Nodes.Add(drive, drive)
Try
node = FindSubFolders(folders, node)
Catch ex As Exception
Console.WriteLine(ex.Message)
Continue For
End Try
End If
Next
End If
If Not IsNothing(My.Settings.Folders) Then
If ListBox1.Items.Count < 1 Then
For Each col As String In My.Settings.Folders
ListBox1.Items.Add(col)
Next
End If
Else
My.Settings.Folders = New StringCollection
End If
Catch ex As Exception
Logs.Add("04", ex.Message)
End Try
Logs.Add("01", "Loaded.")
End Sub
Thanks for the help! :)
Here are a few tips:
One thing you can do to speed things up is to make sure the TreeView-control does not have to repaint itself every time you add an item to it.
Before adding any item, run Treeview1.BeginUpdate and after you have added all items run Treeview1.EndUpdate
If possible, get the directories as an array, and use the node.addrange to add a whole range of directiryes at once.
From MSDN:
To maintain performance while items
are added one at a time to the
TreeView, call the BeginUpdate method.
The BeginUpdate method prevents the
control from painting until the
EndUpdate method is called. The
preferred way to add items to a tree
view control is to use the AddRange
method to add an array of tree node
items to a tree view. However, if you
want to add items one at a time, use
the BeginUpdate method to prevent the
TreeView control from painting during
the add operations. To allow the
control to resume painting, call the
EndUpdate method when all the tree
nodes have been added to the tree
view.
Check out this question for a (maybe) more easy way to fetch the subfolders:
Get all folder / directories list in VB.net