What's wrong with this iterating over a collection code? - vba

I want to pass the items from ListBox1 to ListBox2, and delete them from LisBox1. It throws a null exception at "lb2.Items.Add(item)" but can't find out why. It works fine with just one item though
I tried doing a "for each item in lb1.items... lb2.items.add(item) + lb1.items.remove(item)" but it wouldn't work for you can't modify a list while iterating over it or an exception will be thrown. Also tried other different approaches but couldn't make it work
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
If lb1.Items.Count > 0 Then
Dim itemsAPasar((lb1.Items.Count - 1)) As Object
For Each item In lb1.Items
itemsAPasar(UBound(itemsAPasar)) = item
Next
For Each item In itemsAPasar
lb2.Items.Add(item)
Next
For Each item In itemsAPasar
lb1.Items.Remove(item)
Next
End If
End Sub

It looks like you could simplify your approach a bit...
Dim itemsToMove = lb1.Items.ToList()
For Each item in itemsToMove
lb1.Items.Remove(item)
lb2.Items.Add(item)
Next

Related

How to get item text from CheckedListBox to ListBox without repeating the before text? VB.net

I have a CheckedListBox of 10 names A to J (it's a string but here I'm just using abc), and I want to make it so that every time a user check something, the text will be shown in the listbox simultaneously. I have tried this:
For i = 0 To chklistbxDrugAvailableList.Items.Count - 1
Dim drugs As String = CType(chklistbxDrugAvailableList.Items(i), String)
If chklistbxDrugAvailableList.GetItemChecked(i) Then
listbxYourOrder.Items.Add(drugs)
End If
'drugs = nothing
Next
But when I check A,B,C the text in the Listbox got repeated like so:
A
A
B
A
B
C
I don't know why this is happening. I didn't declare drugs as an array. Even when I used the drugs = nothing, it still give repeated values.
What should I do? Sorry if this is a noob question.
Here is how I would do the whole thing:
Private Sub CheckedListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles CheckedListBox1.ItemCheck
'Clear the current ListBox contents.
ListBox1.Items.Clear()
'Get the indexes of the currently checked items.
Dim checkedIndices = CheckedListBox1.CheckedIndices.Cast(Of Integer)()
'Add the current index to the list if the item is being checked, otherwise remove it.
checkedIndices = If(e.NewValue = CheckState.Checked,
checkedIndices.Append(e.Index),
checkedIndices.Except({e.Index}))
'Get the checked items in order and add them to the ListBox.
ListBox1.Items.AddRange(checkedIndices.OrderBy(Function(n) n).
Select(Function(i) CheckedListBox1.Items(i)).
ToArray())
End Sub
or like this:
Private Sub CheckedListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles CheckedListBox1.ItemCheck
'Get the indexes of the currently checked items.
Dim checkedIndices = CheckedListBox1.CheckedIndices.Cast(Of Integer)()
'Add the current index to the list if the item is being checked, otherwise remove it.
checkedIndices = If(e.NewValue = CheckState.Checked,
checkedIndices.Append(e.Index),
checkedIndices.Except({e.Index}))
'Get the checked items in order and add them to the ListBox.
ListBox1.DataSource = checkedIndices.OrderBy(Function(n) n).
Select(Function(i) CheckedListBox1.Items(i)).
ToArray()
End Sub
The difference is that, because the second option uses data-binding, the first item in the ListBox will be selected by default.
The reason that it needs to be a bit verbose is that the ItemCheck event is raised before the item is checked or unchecked. For that reason, we need to get the currently checked items first and then either add or remove the current item, depending on whether the current item is being checked or unchecked.
EDIT:
Here's an option that doesn't require clearing the ListBox:
Private Sub CheckedListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles CheckedListBox1.ItemCheck
Dim item = CheckedListBox1.Items(e.Index)
If e.NewValue = CheckState.Checked Then
ListBox1.Items.Add(item)
Else
ListBox1.Items.Remove(item)
End If
End Sub
The issue with that is that the items will appear in the ListBox in the order you checked them, rather than the order they appear in the CheckedListBox. The other options keep the items in the same order in both lists.
If I understand your question I would this:
lstbxYourOrder.Items.Clear()
For Each l As String In chklistbxDrugAvailableList.CheckedItems
listbxYourOrder.Items.Add(l)
Next

Select all items from Listbox, add them all only once to a Listview through BackgroundWorker

I am having some problems lately with selecting ALL items(ONLY once!) from a listbox and adding them to a listview. I am using a backgroundworker to handle this task due to big content the listview will contain and to avoid GUI freezing while performing this task.
Ok, so here is the BackgroundWorker_ProgressChanged code :
For Each item In ListBox3.SelectedItems
listView1.Items.Add(ListBox3.SelectedItem, ImageList1.Images.Count - 1).SubItems.Add("Test")
ListView1.Items("Test").SubItems.Add("")
Next
For Each item As ListViewItem In ListView1.SelectedItems
Next
End Sub
The above written code displays items in listview, but ONLY if the user selects a certain item from the Listbox3 and displays infinite times the selected items from the listbox, I want it display ONLY ONCE all selected items from the Listbox, in the Listview. I want to select ALL items automatically, without user intervention, I have tried several methods which have failed.
Can someone please provide a solution to this issue ? Thanks.
I just tested and it seems that getting items from a ListBox on a secondary thread is not an issue, so I was wrong about that. Adding/setting items definitely would be though, so you'd need to add the items to the ListView on the UI thread. Here's some example code that just worked for me:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim outputItems As New List(Of ListViewItem)
For Each inputItem As String In ListBox1.Items
outputItems.Add(New ListViewItem(inputItem))
Next
e.Result = outputItems
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Dim items = DirectCast(e.Result, List(Of ListViewItem))
ListView1.Items.AddRange(items.ToArray())
End Sub
The problem is all the screen redrawing. I am guessing you may not need a background worker if you limit the screen redraw.
I used a list for the ListViewItems since I don't know how many there will be. Had to convert the list to an array to use .AddRange.
As a matter of fact just the .BeginUpdate and .EndUpdate might speed things up enough to be acceptable.
I am not sure about the use of ImageList so you might have to play around with that.
Private Sub OpCode()
Dim items = From itm In ListBox1.Items 'or ListBox1.SelectedItems
Select CStr(itm)
Dim lvItems As New List(Of ListViewItem)
Dim imageIndex As Integer = ImageList1.Images.Count - 1
For Each item In items
Dim lvItem As New ListViewItem(item, imageIndex)
lvItems.Add(lvItem)
Next
'ListView1.BeginUpdate()
ListView1.Items.AddRange(lvItems.ToArray)
'ListView1.EndUpdate()
End Sub

How can I mirror checked items from one CheckedListBox to another?

I have 2 checklistbox controls and want the items in the second control to mirror the checked state of those in the first. For example:
Checklistbox1 = APPLE, MANGGO, BANANA, STRAWBERRY, GRAPE
Then i checked manggo and grape.
checklistbox2 = 0,1,0,0,1
How do I go about this?
This should accomplish what you want. Note that if you have a CheckedListBox2_SelectedIndexChanged event, you could get unexpected results, as this code will trigger it.
Private Sub CheckedListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CheckedListBox1.SelectedIndexChanged
Dim i As Integer
For i = 0 To CheckedListBox2.Items.Count - 1
CheckedListBox2.SetItemChecked(i, False)
Next
For Each i In CheckedListBox1.CheckedIndices
CheckedListBox2.SetItemChecked(i, True)
Next
End Sub
If you have a large list, this might be a bit more efficient, but you end up with the same result.
Private Sub CheckedListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CheckedListBox1.SelectedIndexChanged
Dim i As Integer
For i = 0 To CheckedListBox2.Items.Count - 1
CheckedListBox2.SetItemChecked(i, CheckedListBox1.GetItemCheckState(i))
Next
End Sub
Also you might want to have the checkonclick property of your listboxes set to true to save you having to click the item twice - and it yields more consitent results with both my code and the code from #josh , but if you need to do anything else when you're selecting an item, you might want it turned off

ObjectListView loop through items and delete selected

I am trying to count selected items and delete selected items with a loop by using an ObjectListView (found on sourceforge). What I have is not working
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If lstObjectMain.Items.Count > 0 Then
If lstObjectMain.SelectedItems.Count > 0 Then
Debug.Print(lstObjectMain.SelectedItems.Count)
lstObjectMain.SelectedItem.Remove()
End If
End If
End Sub
Try this:
If lstObjectMain.SelectedObjects IsNot Nothing Then
For Each a In lstObjectMain.SelectedObjects
lstObjectMain.RemoveObject(a)
Next
End If
Keep in mind that this is only removing items from the view.
It is better to modify underlying model, and then hit again lstObjectMain.SetObjects()

Using For each loop without try-catch in Visual basic 2012

I have this simple code:
Public Class Form1
Dim strFriends(4) As String
Private Sub ArrayElement_Click(sender As Object, e As EventArgs) Handles ArrayElement.Click
ClearList()
'Try
For Each item As String In strFriends
lstFriends.Items.Add(item)
Next
'Catch
'End Try
End Sub
Private Sub ClearList()
lstFriends.Items.Clear()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
strFriends(0) = "tom"
strFriends(1) = "kate"
strFriends(2) = "bill"
strFriends(3) = "harry"
End Sub
End Class
If the try-catch is removed, i get System.ArgumentNullException is it compulsory to use try catch block to use For Each?
You are declaring a 5 element array: Dim strFriends(4) As String. In Vb.NET the number represents the max index on the array, not the number of elements.
But you're declaring only 4 elements. So in the foreach block, the last element is the default value for strings, which is Nothing, being unable to add it to the listview (or whatever).
You can check for every item on the array to be valid, like others suggested, or correct your code.
Try this, for example:
strFriends = New String() {"tom", "kate", "bill", "harry"}
You can use a List, too:
Dim strFriends As New List(Of String)()
strFriends.Add("tom")
strFriends.Add("kate")
strFriends.Add("bill")
strFriends.Add("harry")
Or you can check each item before adding. You are also not filling the last element and that is the reason for the exception.
If item IsNot Nothing Then
'add item
End If
Try this:
If Not String.IsNullOrEmpty(item) Then
' Add item
End If
UPDATE:
You can check to see if the array has anything in it, like this:
If strFriends.Length > 0 Then
' Do something with array
End If
No, a for each loop does not require a try block. Using try-catch for flow control is a bug. Instead, test to ensure that elements are not Nothing before adding them.