How to remove selected items from a listbox - vb.net

This is for a VB.NET 4.5 project in VS2015 Community.
I am trying to remove certain selected items from a listbox, but only if the selected item meets a condition. I've found plenty of examples on how to remove selected items. But nothing that works with a condition nested in the loop going through the selected items (at least, I can't get the examples to work with what I'm trying to do...)
Here's my code:
Dim somecondition As Boolean = True
Dim folder As String
For i As Integer = 0 To lstBoxFoldersBackingUp.SelectedItems.Count - 1
If somecondition = True Then
folder = lstBoxFoldersBackingUp.SelectedItems.Item(i)
Console.WriteLine("folder: " & folder)
lstBoxFoldersBackingUp.SelectedItems.Remove(lstBoxFoldersBackingUp.SelectedItems.Item(i))
End If
Next
The console output correctly shows the text for the current iteration's item, but I can't get the Remove() to work. As the code is now, I get the console output, but the listbox doesn't change.

Removing items changes the index position of the items. Lots of ways around this, but from your code, try iterating backwards to avoid that problem. You should also remove the item from the Items collection, not the SelectedItems collection:
For i As Integer = lstBoxFoldersBackingUp.SelectedItems.Count - 1 To 0 Step -1
If somecondition = True Then
folder = lstBoxFoldersBackingUp.SelectedItems.Item(i)
Console.WriteLine("folder: " & folder)
lstBoxFoldersBackingUp.Items.Remove(lstBoxFoldersBackingUp.SelectedItems(i))
End If
Next

You can simply use this in order to remove a selected item from the listbox
ListBox1.Items.Remove(ListBox1.SelectedItem)
I hope this was helpful.

This will not work:
ListBox1.Items.Remove(ListBox1.SelectedItem)
You need to define exactly which items will be deleted, so the next works fine for me:
ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
However it removes one line every time it is called.

Related

Saving Custom Document Properties in a Loop

I'm trying to save the values of data that have been input into my form. There are a total of about 50 different fields to save across 5 different agents, so I loaded the data into arrays.
I've tried saving the fields in a loop, but it doesn't seem to work in a loop, only if each field has a separate line, which is a lot of code and messy. The Ag1Name, Ag2Name and Ag3Name are the names of my textboxes that the user enters to populate the form.
Sub LoadAndSaveData()
NumberofAgents = 3
Dim AgentName(3) as String
AgentName(1) = Ag1Name.Value
AgentName(2) = Ag2Name.Value
AgentName(3) = Ag3Name.Value
For Count = 1 To NumberOfAgents
With ActiveDocument.CustomDocumentProperties
.Add Name:="AgentName" & Count, LinkToContent:=False, Value:=AgentName(Count), Type:=msoPropertyTypeString
End With
Next Count
End Sub
The data doesn't get saved to the Custom Document Properties when the code is set up in a loop like the above. Since there are so many values to save and all the data is already in arrays, I would much prefer to use a loop rather than write out a separate line of code for all ~50 of the values. It does seem to work when each field is saved in a separate line of code.
I think this would probably get what you want. You don't really need to count the document properties first, only increment with the ones you want to update. Hopefully the only document properties you want contain the name AgentName in it.
ReDim AgentName(0) As String
Dim P As Long
For Each c In ThisDocument.CustomDocumentProperties
If InStr(1, c.Name, "AgentName", vbTextCompare) > 0 Then
ReDim Preserve AgentName(P)
AgentName(P) = c.Value
P = P + 1
End If
Next c
As a guest I cannot post a comment here, but the code you gave works OK here.
However, there is a problem with creating legacy custom document properties programmatically, because doing that does not mark the document as "changed". When you close the document, Word does not necessarily save it and you lose the Properties and their values.
However, if you actually open up the Custom Document Property dialog, Word does then mark the document as "changed" and the Properties are saved.
So it is possible that the difference between your two scenarios is not the code, but that in one scenario you have actually opened the dialog box to check the values before closing the document and in the other you have not.
If that is the case, here, I was able to change this behaviour by adding the line
ActiveDocument.Saved = False
after setting the property values.
If you do not actually need the values to be Document Properties, it might be better either to use Document Variables, which are slightly easier to use since you can add them and modify them with exactly the same code, or perhaps by storing them in A Custom XML Part, which is harder work but can be useful if you need to extract the values somewhere where Word is not available.
You can make this even easier by looping the controls on the UserForm, testing whether the control name contains "Ag" and, if it does, create the Custom Document Property with the control's value - all in one step.
For example, the following code sample loops the controls in the UserForm. It tests whether the controls Name starts with "Ag". If it does, the CustomDocumentProperty is added with that control's value.
Sub LoadAndSaveData()
Dim ctl As MSForms.control
Dim controlName As String
For Each ctl In Me.Controls
controlName = ctl.Name
If Left(controlName, 2) = "Ag" Then
With ActiveDocument.CustomDocumentProperties
.Add Name:=controlName, LinkToContent:=False, value:=ctl.value, Type:=msoPropertyTypeString
End With
End If
Next
End Sub
I feel a little stupid... I just realized that the reason that the code wasn't working was that the variable NumberofAgents was not being calculated correctly elsewhere in my code. I've got it working now. Thanks for your thoughts!

Save and Load dynamically generated checkedlistbox items

I'm pretty new to VB, so bear with me. On form load, a list of values is pulled from a database, and the values are used to generate a list of checkedlistbox items.
A user will select which checkboxes they want, and then click save, at which point the checked items are stored to a system.collections.arraylist type user setting.
I am able to save the values of the checkboxes to the user settings, but I'm trying to load the saved settings the next time the application is opened, but I'm unsure how to do this. The only way I've been able to do it only gets the checked items, which isn't what I want.
Here's what I'm using to save the items:
Dim list As New ArrayList
For i As Integer = 0 To CheckedListBox1.Items.Count - 1
list.Add(CheckedListBox1.Items(i))
i = i + 1
Next
My.Settings.selectedlistbox = list
My.Settings.Save()
And this is what I've currently got to load the settings, which isn't working at all:
Dim counter As Integer
counter = 0
While counter <= My.Settings.selectedlistbox.Count - 1
CheckedListBox1.SetItemChecked(counter, list(counter))
counter = counter + 1
End While
On a related note, I'm calling the above code prior to the code where the database connection is opened, so I'm thinking this may be a problem too, since I think the checkboxes won't be able to be accessed until they're actually in the application. I tried placing the code after where the checkedlistbox is loaded, but that didn't work either.
Assume that My.Settings.Useropts is a StringCollection, to skip the ArrayList:
' CLEAR OUT OLD SELECTIONS so they do not accumulate
My.Settings.Useropts.Clear
' save items checked
For i As Integer = 0 To CheckedListBox1.CheckedIitems.Count - 1
My.Settings.Useropts.Add(CheckedListBox1.CheckedItems(i))
Next
My.Settings.Save()
Or iterate the checkedindicies collection:
For i As Integer = 0 To CheckedListBox1.CheckedIndicies.Count - 1
My.Settings.Useropts.Add(CheckedListBox1.Items(CheckedIndicies(i))
Next
My.Settings.Save()
' reload from settings
Dim ndx As Integer
For Each s as string in My.Settings.Useropts
ndx = CheckedListBox1.Items.indexOf(s) ' find this string in the list
' if NDX is -1 then the item does not exist;
If ndx <> -1 then
' set the check for the related INDEX, if found:
CheckedListBox1.SetItemChecked(ndx, True)
Else
CheckedListBox1.SetItemChecked(ndx, False)
End If
Next
If there arent default items in the collection you may need to initialize it on FormLoad. The text for the CheckedList box cant change or items wont be found.
Since the Items collection can store Objects, you could devise a Class of {Name, Key} where key uniquely identifies each item, which Name might change as needed. Store the Keys, then find them in the CLB to set that Item's checkstate as above
Fixed typo in SetItemChecked(n, --> `SetItemChecked(ndx,
Added warning about clearing old settings, annotated the restore loop

Delete from ListBox items in a Loop

Each Item information get from a file which specified with the name of that item. Even though the selected items will be deleted properly in the listbox but the file of next item will be deleted. I don't know if problem is for Index or anyother part
SourceDir = "c:\"
For Each itemIndex In listHouse.SelectedIndices()
itemIndex = listHouse.SelectedIndices(0)
listHouse.Items.RemoveAt(itemIndex)
MsgBox(listHouse.Items.Item(itemIndex).Text & "R.txt")
File.Delete(SourceDir & listHouse.Items.Item(itemIndex).Text & "R.txt")
Next
You cannot add or remove items from a collection while inside a For/Each loop.
Each time you RemoveAt(n), you change the make-up of the collection you are looping. If you remove item #4, item 5 moves up to its slot. Then after Next, your code will be looking at position 5, and be looking at what was originally #6 - item 5 would be skipped entirely. To prevent this every-other one bug/issue, an exception is thrown when using a For/Each.
Secondly, your File.Delete code is trying to reference the item you just removed!
To iterate a collection and remove some/all items one by one, loop backwards using an indexer. This removes from the end of the collection so nothing can "move up". Like this:
' loop backwards using For n
For n as Integer = listHouse.SelectedIndices.Count -1 to 0 Step-1
itemIndex = listHouse.SelectedIndices(n)
MsgBox(listHouse.Items.Item(n).Text & "R.txt")
File.Delete(SourceDir & listHouse.Items.Item(n).Text & "R.txt")
' also remove the thing AFTER you have used it to delete
listHouse.Items.RemoveAt(n)
Next
If you tried to use a forward For n loop with this sort of action, you'd get the every-other one issue.

VB.NET - Loop skipping Items in ListBox

I have a listbox with directories (not local, it is over a network) listed in it.
I am trying to loop through that listbox and remove any empty directories.
However, I noticed that it just skips some empty directories.. I found that if I ran the loop 3 - 5 times it will get them all, but that isn't very efficient.
My Loop:
Dim i As Integer
i = 0
While i < ListBox1.Items.Count
If IO.Directory.GetFiles(ListBox1.Items.Item(i), "*.*").Length = 0 Then
ListBox1.Items.RemoveAt(i)
End If
i = i + 1
End While
So I was just wondering if there is a more efficient way to check the contents of the directory or another way i could achieve this without having to run the loop multiple times.
ListBox1.Items.Count is getting reevaluated every time, making your loop get shorter as time goes on missing some items. A quick solution could be just mark which items you want to remove as you loop.
Dim toRemove As New List(Of ListBoxItem)
For Each item as ListBoxItem in ListBox1.Items
If IO.Directory.GetFiles(item, "*.*").Length = 0 Then
toRemove.Add(item)
End If
Next
For Each item as ListBoxItem in toRemove
ListBox1.Items.Remove(item)
Next
(there is probably a better way, but that's a quick solution off the top of my head)

Not all controls in collection are being copied

I'm a bit confused here. I'm copying all the controls from one form to a panel on the main form and for some reason only about half of them copy.
Private Sub switchComponent()
Dim selection As String = TreeView1.SelectedNode.Text
Panel1.Controls.Clear()
Dim query = From cont In serverDic(selection).Controls
Select cont
For Each copier As Control In query
Panel1.Controls.Add(copier)
Next
End Sub
serverDic is defined as:
Dim serverDic As New Dictionary(Of String, frmServer)
When stepping through the code, serverDic(selection).Controls has 12 elements, but only 6 of them get copied. Next time this gets called, only 3 get copied. Does Panel1.Controls.clear() somehow kill the references?
EDIT: Just to show that there are infact 12 elements in the collection:
The problem here is that you are iterating over a collection that you are changing. When you add a Control to an container it is implicitly removed from it's previous parent and hence query. This is why you see exactly half of the items get removed.
With most collections this would be more apparent because they would throw if modified during an enumeration. The primary source of query here though is ControlCollection which does allow for modifications while enumerating.
To fix this problem just add the following line before the For Each loop.
query = query.ToList()