Index out of range in listbox VB.NET - vb.net

Consider the following code :
Private Sub DelButton_Click(sender As Object, e As EventArgs) Handles DelButton.Click
If OrderListBox.Text = "" = False Then
For i As Integer = OrderListBox.SelectedIndex To arr.Count - 1
arr.RemoveAt(i)
Next
OrderListBox.Items.Remove(OrderListBox.SelectedItem)
End If
calculate()
End Sub
The program crashes at arr.RemoveAt(i) and displays the following error:
Index was out of range. Must be non-negative and less than the size
of the collection.

First of all, please note that in VB.NET and C#, FOR loops are implemented differently!
In VB.NET, it works like this:
BEFORE the loop starts, you are determining start and end of the loop:
Start = OrderListBox.SelectedIndex
End = arr.Count-1
Then, the loop starts.
It is important to know, that in VB.NET, the end of the loop is NOT calculated again anymore. This is an important difference to C#. In C#, the end of the loop is calculated before each single loop.
And now, in the loop, you are DELETING records from the array.
Therefore, the count of records in the array is DECREASING.
However, your loop is going on, since you have calculated the count of records in the array before the loop started.
Therefore, you are going beyond the range of the array.
You could rewrite your code as follows:
Dim i as Integer
i = OrderListBox.SelectedIndex
while i < arr.Count
arr.RemoveAt(i)
Next
This article covers details about the for loop in VB.NET, especially the section "Technical Implementation": https://msdn.microsoft.com/en-us/library/5z06z1kb.aspx

This error will be thrown when you try to remove an item at an index greater than the size of the collection. i.e when i is greater than arr.Count - 1.
You should make sure that OrderListBox.SelectedIndex is not greater than arr.Count - 1. Because if it does, you remove an item that, well, does not exists.
This code is actually displayed in the MSDN docs. As stated, you should do something like this:
Private Sub RemoveTopItems()
' Determine if the currently selected item in the ListBox
' is the item displayed at the top in the ListBox.
If listBox1.TopIndex <> listBox1.SelectedIndex Then
' Make the currently selected item the top item in the ListBox.
listBox1.TopIndex = listBox1.SelectedIndex
End If
' Remove all items before the top item in the ListBox.
Dim x As Integer
For x = listBox1.SelectedIndex - 1 To 0 Step -1
listBox1.Items.RemoveAt(x)
Next x
' Clear all selections in the ListBox.
listBox1.ClearSelected()
End Sub 'RemoveTopItems

ListBox.SelectedIndex will return a value of negative one (-1) is returned if no item is selected.
You didn't check it in your code.
Change your code to this:
If OrderListBox.SelectedIndex >= 0 And OrderListBox.Text = "" = False Then
EDIT
Your code is this:
For i As Integer = OrderListBox.SelectedIndex To arr.Count - 1
arr.RemoveAt(i)
Next
Let's say your OrderListBox contains 3 items : [A, B, C] and the SelectedIndex is 0
Then your code will:
Remove (0) ===> [B, C]
Remove (1) ===> [B]
Remove (2) ===> Exception!
You need to reverse the loop
For i As Integer = arr.Count - 1 To OrderListBox.SelectedIndex Step -1
arr.RemoveAt(i)
Next

Related

How do I loop through a DataGridView where RowCount / Rows.Count changes

I have a VB.Net Form with a DataGridView where I wish to change between a Detailed View and a General View.
The DataGridView shows the Distance and Estimated Time between places - called the Route Leg Data and is termed the General View
I have implemented a CheckBox to select between the general and detailed view. When the CheckBox is Checked, I am trying to loop through all of the Route Leg entries and insert the Route Steps which is the Detailed information about the Leg entries.
I have tried various loop options: For..Next, For Each...Next, While...End While, and only the first row (Route Leg) gets processed, even if I have 5 more Route Leg entries.
Important: Keep in mind that when the Detail View is selected, the DataGridView row count increments for every new Route Step entry that gets inserted.
I tried to use both dgv.RowCount and dgv.Rows.Count but I keep getting the same result.
I have added some code to show what I am trying to achieve. Any help or guidance will be much appreciated.
'Show / Hide Route Step Data
Private Sub chkShowRouteStep_CheckedChanged(sender As Object, e As EventArgs) Handles chkShowRouteStep.CheckedChanged
Try
If chkShowRouteStep.Checked Then
'Show Route Steps
For i As Integer = 0 To dgvQuote.RowCount - 1
txtRowCount.Text = i
If dgvQuote.Rows(i).Cells(0).Value.ToString <> "" Then
For j As Integer = 1 To 5
i += 1
dgvQuote.Rows.Insert(i, "Step")
'dgvQuote.Rows.Insert(j + i, "Step")
Next
End If
Next
Else
'Hide Route Steps - WORKS GREAT
For i As Integer = dgvQuote.RowCount - 1 To 0 Step -1
If dgvQuote.Rows(i).Cells(0).Value.ToString = "Step" Then
dgvQuote.Rows.RemoveAt(i)
End If
Next
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
My impatience, as well as a refocus, provided me with the answer.
My focus was always on the DataGridView and the feedback from it with regards to the number of rows contained in the control.
However, the execution of a loop took a snapshot of the number of rows at the start of the loop execution and did NOT update the number of rows every time rows were added to the DataGridView.
I took a different approach where I compared my current loop number (i += 1) with the current number of rows in the DataGridView (dgv.Rows.Count - 1) thereby forcing a recheck on the number of rows. This means that any new rows added to the DataGridView will now be counted in.
I added a trigger (True/False) to be set to true if the last row in the DataGridView was reach and the While...End While loop was exited.
'Show / Hide Route Step Data
Private Sub chkShowRouteStep_CheckedChanged(sender As Object, e As EventArgs) Handles chkShowRouteStep.CheckedChanged
Try
Dim lastRow As Boolean = False 'This will get set when we have reached the last row in the DGV
Dim i As Integer = 0
If chkShowRouteStep.Checked Then
'Show Route Steps
While lastRow <> True
txtRowCount.Text = i
If dgvQuote.Rows(i).Cells(0).Value.ToString <> "" Then
For j As Integer = 1 To 2
'i += 1
dgvQuote.Rows.Insert(i + j, "", "Step")
Next
End If
'Check to see if we have reached the last row, set lastRow to TRUE if it is the last row
If i = dgvQuote.Rows.Count - 1 Then
lastRow = True
End If
i += 1
End While
Else
'Hide Route Steps - WORKS GREAT
For x As Integer = dgvQuote.RowCount - 1 To 0 Step -1
If dgvQuote.Rows(x).Cells(1).Value.ToString = "Step" Then
dgvQuote.Rows.RemoveAt(x)
End If
Next
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
The result is as follows and error free:
Before
After
Since the comment in the code says you want to hide route steps, why not do just that? Instead of removing and inserting rows, populate the grid with everything and then use the checkbox to set the rows .Visible property?

VBA Runtime Error 35600 when removing items from ListView

I would like to ask for help regarding a Runtime Error 35600 "Index out of bounds".
I am trying to delete all Items from a Multicolumn-ListView that do not match a Combobox-Value.
However, it seems that during the deletion-process, my code reaches a Point where the listitems-index is smaller than the index of the selected item.
Does anyone know how I can solve that? Here is my take on it:
Private Sub ComboBox1_Change()
Dim i As Integer
Dim strSearch As String
strSearch = Me.ComboBox1
For i = 1 To ListView1.listItems.Count
If Me.ListView1.listItems(i).SubItems(3) = strSearch Then
Me.ListView1.listItems(i).Checked = True
End If
Next i
For i = 1 To ListView1.listItems.Count
If ListView1.listItems(i).Checked = False Then
Me.ListView1.listItems.Remove (ListView1.selectedItem.Index)
End If
Next i
End Sub
You could try remove them in reverse order (so only for the second loop); I think in basic it would look like:
For i = ListView1.listItems.Count To 0 Step -1
Probably the counter is not re-evaluated after every loop and thus will be higher than the number of elements causing a too high number (more than the number of list items present resulting in an index out of bounds exception).
'Removing part
With ListView1
For i = .ListItems.Count To 1 Step -1
If Not .ListItems(i).Checked Then
.ListItems.Remove i
End If
Next
End With

Check if listbox contains textbox

I know I could use .FindString for this but for some reason it is not working.
Basically,if listbox items contains just a PART of textbox text,it does action.
Here's the example of not-working code :
Dim x As Integer = -1
x = ListBox1.FindString(TextBox1.Text)
If x > -1 Then
'dont add
ListBox2.Items.Add("String found at " & x.ToString)
Else
End If
The FindString method returns the first item which starts with the search string (MSDN). If you want to match the whole item, you would have to use FindStringExact (MSDN). If you want to perform more complex searches, you would have to iterate through all the elements in the ListBox.
UPDATE:
Code delivering the exact functionality expected by the OP.
For i As Integer = 0 To ListBox1.Items.Count - 1
If (ListBox1.Items(i).ToString.Contains(TextBox1.Text)) Then
ListBox2.Items.Add("String found at " & (i + 1).ToString) 'Indexing is zero-based
Exit For
End If
Next

Multi Select List Box

I have a list box on a form and it works fine for what I want to do.
I am wanting to edit items on the form, this means populating the listbox and then selecting the relevant items.
My listbox contains a list of item sizes, i want to select the sizes which belong to the item being edited.
PLease can someone give me some pointers.
I tried me.lstItemSizes.SetSelected(i,true) but this only works for a single item.
Any help wil be much appreciated.
My Code:
Private Sub SelectItemSizes(ByVal itemID As Integer)
Dim itemSizes As IList(Of ItemSize) = _sizeLogic.GetItemSizes(itemID)
Me.lstItemSizes.SelectionMode = SelectionMode.MultiExtended
If (itemSizes.Count > 0) Then
For i As Integer = 0 To Me.lstItemSizes.Items.Count - 1
For x As Integer = 0 To itemSizes.Count - 1
If (CType(Me.lstItemSizes.Items(i), PosSize).SizeID = itemSizes(x).SizeID) Then
Me.lstItemSizes.SetSelected(i, True)
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Next
Next
End If
End Sub
Did you set the selectionmode to multi?
You need to specify that in order to allow multiple selections.
Then you can do:
Dim i as Integer=0
For i=0 To Me.listBox.SelectedItems.Count -1
'display the listbox value
next i
Here is a screen shot:
After you set the property on the listbox then call setselected based on the values you want selected.
me.lstItemSizes.SetSelected(3,true)
me.lstItemSizes.SetSelected(4,true)
me.lstItemSizes.SetSelected(9,true)
Here you can add 20 numbers and only select the even.
Dim i As Integer
'load the list with 20 numbers
For i = 0 To 20
Me.ListBox1.Items.Add(i)
Next
'now use setselected
'assume only even are selected
For i = 0 To 20
If i Mod 2 = 0 Then
Me.ListBox1.SetSelected(i, True)
End If
Next
3rd edit
Look at the way you are looping, lets assume I create a list of integers, my vb.net is rusty I mainly develop in C#. But assume you did this:
Dim l As New List(Of Integer)
l.Add(2)
l.Add(6)
l.Add(20)
You only have three items in your list, so first loop based on the items on your list, then within the items in your listbox, you have it vice versa. Look at this:
Dim i As Integer
Dim l As New List(Of Integer)
l.Add(2)
l.Add(6)
l.Add(20)
'load the list with 20 numbers
For i = 0 To 20
Me.ListBox1.Items.Add(i)
Next
Dim lCount As Integer = 0
For lCount = 0 To l.Count - 1
For i = 0 To 20
If i = l.Item(lCount) Then
Me.ListBox1.SetSelected(i, True)
Exit For
End If
Next
Next
In the code my l is a list of just 3 items: 2, 6, and 20.
I add these items to l which is just a list object.
So now I have to loop using these 3 numbers and compare with my listbox. You have it the opposite you are looping on your listbox and then taking into account the list object.
Notice in my for loop that once the item in my list is found I no longer need to loop so I exit for. This ensures I dont overdue the amount of looping required. Once the item is found get out and go back to the count of your list object count.
After running my code here is the result
You have to change the ListBox.SelectionMode property in order to enable multiple-selection.
The possible values are given by the SelectionMode enum, as follows:
None: No items can be selected
One: Only one item can be selected
MultiSimple: Multiple items can be selected
MultiExtended: Multiple items can be selected, and the user can use the Shift, Ctrl, and arrow keys to make selections
So, you simply need to add the following line to the code you already have:
' Change the selection mode (you could also use MultiExtended here)
lstItemSizes.SelectionMode = SelectionMode.MultiSimple;
' Select any items of your choice
lstItemSizes.SetSelected(1, True)
lstItemSizes.SetSelected(3, True)
lstItemSizes.SetSelected(8, True)
Alternatively, you can set the SelectionMode property at design time, instead of doing it with code.
According to MSDN, SetSelected() can be used to select multiple items. Simply repeat the call for each item that needs to be selected. This is the example they use:
' Select three items from the ListBox.
listBox1.SetSelected(1, True)
listBox1.SetSelected(3, True)
listBox1.SetSelected(5, True)
For reference, this is the MSDN article.
Because my code had the following loops:
For i As Integer = 0 To Me.lstItemSizes.Items.Count - 1
For x As Integer = 0 To itemSizes.Count - 1
If (CType(Me.lstItemSizes.Items(i), PosSize).SizeID = itemSizes(x).SizeID) Then
Me.lstItemSizes.SetSelected(i, True)
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Next
Next
The first loop loops through the available sizes and the second loop is used to compare the item sizes.
Having the following code:
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Meant that even if item i became selected, it could also be deselected.
SOLUTION:
Remove Me.lstItemSizes.SetSelected(i, False) OR Include Exit For

Not able to add values in second Combobox

Here is my code.
for example TextBox1.Text= 12,34,45,67,67
Dim process_string As String() = TextBox1.Text.Split(New Char() {","})
Dim process As Integer
For Each process In process_string
Combo1.Items.Add(process)
count = count + 1
Next process
total_process.Text = count
End If
Dim array(count) As String
Dim a As Integer
For a = 0 To count - 1
array(a) = Combo1.Items(a).ToString
Next
a = 0
For a = count To 0
Combo2.Items.Add(array(a).ToString)
Next
i want to add values in reversed order in combobox2 that are available in combobox1
but when i run the application the second combobox remains empty and not showing any value.
You've specified this for loop
For a = count To 0
But you need to add STEP -1 to go backwards like that.
For a = count To 0 Step -1
2 things. First of all, K.I.S.S. Keep it simple stupid
For i As Integer = ComboBox1.Items.Count - 1 To 0 Step -1
ComboBox2.Items.Add(ComboBox1.Items(i))
Next
second: It didn't work because you forgot the Step -1 on your last loop
~~~~~~~~~~~~~~Edit~~~~~~~~~~~~~~
Sorting the data in a combo box should be done with the sorted property on a combo box
ComboBox3.Sorted = True
Sorting the data in reverse order should be done with arrays as you were trying to do before. The following code should suffice:
Dim List As ArrayList = ArrayList.Adapter(ComboBox3.Items)
List.Sort()
List.Reverse()
ComboBox4.Items.AddRange(List.ToArray)
If you wanted to get creative, you could potentially create your own combo box class and make your own version of the sorted property that allows for "sort alpha", "sort numeric", "sort alpha Desc", and "sort numeric desc" and perhaps some other options. But I'd only do that if you were going to use this in a lot of places.