InvalidArgument=Value of ' ' is not valid for 'index' (' ' inside number) - vb.net

This is my first question. I can't solve this error for 2 weeks.
In order to solve the problem signed up.
This is my vb code.
Try
For i As Integer = 0 To ListBox1.Items.Count - 1 Step 1
For j As Integer = 0 To ListBox2.Items.Count - 1 Step 1
If ListBox1.Items(i).ToString().Equals(ListBox2.Items(j).ToString()) = True Then
ListBox1.Items.RemoveAt(i)
End If
Next
Next
Catch ex As Exception
MsgBox("LOAD ERROR: " + ex.Message, vbCritical, "ERROR")
End Try
error :
InvalidArgument=Value of '20' is not valid for 'index'(' ' is varient.)
Project has no problems except for this error

Try this:
Dim items = ListBox1.Items.Where(Function(item) ListBox2.Items.Contains(item)).ToList()
For Each item in items
ListBox1.Remove(item)
Next

When I run your code, I receive a different exception, argument out of range...and that is caused by deleting items from an indexed collection while you're iterating through it. For example, let's say listbox1 has 10 items in it. If you find item number 1 in listbox2 and delete it, now you only have 9 items left in listbox1. The problem is, when you entered your loop, you told it to loop 10 items, and it will still try to do that. At some point, if any items are deleted, this loop will throw an exception...so you will need to change that sooner or later. To mitigate this, step through the collection that you'll be deleting items from backward like this:
For i As Integer = ListBox1.Items.Count - 1 to 0 Step -1
When I run the code with the change shown above, it works as intended and removes the duplicate items from listbox1. Unfortunately, I was unable to reproduce your invalid argument exception. It's odd to see that because usually that exception likes to pop up when using listviews, not listboxes. Perhaps you can edit your post and add a screenshot of the data in your listboxes so it's easier for other people to troubleshoot.

As you remove items from ListBox1 the total item count will decrease (obviously), however the For loop does not respect that. A For loop will only have the right side of To set once, which is done prior to the first iteration.
What you're currently doing is actually equal to this:
Dim a As Integer = ListBox1.Items.Count - 1
For i As Integer = 0 To a Step 1
Dim b As Integer = ListBox2.Items.Count - 1
For j As Integer = 0 To b Step 1
...
Next
Next
The fix for this is simple; create a variable that holds how many items you have removed, then, in an If-statement, check if i is more or equal to the current item count subtracted with how many item's you've removed. If so, exit the loop.
Dim ItemsRemoved As Integer = 0
For i As Integer = 0 To ListBox1.Items.Count - 1 Step 1
If i >= ListBox1.Items.Count - ItemsRemoved Then Exit For
For j As Integer = 0 To ListBox2.Items.Count - 1 Step 1
If ListBox1.Items(i).ToString().Equals(ListBox2.Items(j).ToString()) = True Then
ListBox1.Items.RemoveAt(i)
End If
Next
Next
For future reference you should also always remove/comment out the Try/Catch-statement so you can see where the error occurs and get more detail about it.

The point of my answer is that when you iterating any collection, you should NOT try to modify this collection. In for-loops you run into such trouble. But you can iterate using while-loop with no issues
Try
Dim index As Integer = 0
While index < ListBox1.Items.Count '!! this code based on fact that ListBox1 item Count changes
For j As Integer = 0 To ListBox2.Items.Count - 1 ' <- this is ok because ListBox2 doesn't chage
If string.Equals(ListBox1.Items(index).ToString(), ListBox2.Items(j).ToString()) Then
ListBox1.Items.RemoveAt(index)
Continue While ' no index increase here because if you remove item N, next item become item N
End If
Next
index += 1
End While
Catch ex As Exception
MsgBox("LOAD ERROR: " + ex.Message, vbCritical, "ERROR")
End Try
This is good example of how things actually work. And it shows few techniques

I just selected Build-->Clean solution and it cleaned out the bad elements. This occurred as a result of adding and deleting menu items, without deleting the subroutines of the deleted menu items. As soon as I cleaned the solution, and then ran the project, the error was gone.

Related

Even Odd numbers using Nested For Next Loop VBA

How do I create a For-Next loop determining whether the numbers listed are even or odd
You can try something like this:
Dim i As Integer
For i = 1 To 6
If i mod 2 = 0 Then
'i is even
Else
'i is odd
End If
Next i

IndexOutOfRangeException on Datatable

I have an application where a DAQ system samples data at about 1 kHz which is then written to a DataTable.
The code block below shows part of the code which adds data to the datatable.
Public Sub AddTimeLoadData(DateTime As DateTime, DataPointNo As Integer, ClampForceN As Double, _
TransverseForceN As Double, TransverseDispMm As Double, NumLoadCycles As Integer)
Try
_tblTimeLoadData.AddtblTimeLoadDataRow(NumLoadCycles, TransverseDispMm, TransverseForceN, ClampForceN, DataPointNo, DateTime, _TimeLoadTestRow)
_timRaiseDataAdded.Start()
Catch ex As Exception
Throw New Exception("Time/load data could not be added: " & ex.Message, ex)
End Try
End Sub
Updating the GUI each time AddTimeLoadData is called would be unnecessary, so instead I start a System.Timers.Timer. At the Elapsed event of the timer, I raise the TimeLoadDataUpdated event.
In the GUI another class is listening to this event, and updates the chart. This works good most of the times, but occasionally I get an IndexOutOfRangeException when reading from the data table.
It is pretty clear that this has something to do with synchronization, but I have not yet figured out exactly what might be the issue.
Looking at the code, the index 'i' would not really be able to go out of range.
One idea that came up is that if the for loop reaches last row at the same time as it is being created I might have a problem so I could just try updating the for loop to
For i As Integer = s.Points.Count To sender.tblTimeLoadData.Count - 2
Or maybe there is something else that I have foreseen!?
VB keep in memory the "To" part of the for loop.
Dim c As Integer
c = 3
' This will print 0, 1, 2, 3
For i As Integer = 0 To c
c = 10
Console.WriteLine(i)
Next
You might want to consider using a while loop instead.
Dim c As Integer
c = 3
Dim i As Integer = 0
' Will print 0 to 9
While i < c
c = 10
Console.WriteLine(i)
i += 1
End While
Or properly synchlock everything because even between the While/For line and the Rows(i) line, there is a chance the item will be deleted.
Also, if your timer is there to update the UI. You should look into remove all business rules out of it. Have your ds already populated with the proper data point before doing anything on the UI.

Listbox Insert Issue

I have researched this and can't find a reference anywhere to fix the issue. I have created code to randomize about 20 items from a text document, and put them into a listbox on my form. This is a legal document so, once randomized, it would be read back out into the text document, just in a different order. Each item has a heading: REQUEST FOR ADMISSIONS NO. # (# being whatever number that question was in line). I need those #'s to be in number order (1-20) despite the fact that the questions' order is scrambled. I kept working on that until I got it to insert that line, with an incremented number. But it is incrementing them backwards (see redacted example). Program output The culprit language is below. I think it is in there someplace:
ListBox1.BeginUpdate()
Try
'do with listBox1.Items[i]
For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
ListBox1.Items.Insert(i, "REQUEST FOR ADMISSION NO. " & nxtNum & vbCrLf)
nxtNum += 1
Next
Finally
ListBox1.EndUpdate()
End Try
Note: I was able to get it to work by flipping the list. Not elegant but it works.
ListBox1.BeginUpdate()
Try
'do with listBox1.Items[i]
For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
ListBox1.Items.Insert(i + 1, "REQUEST FOR ADMISSION NO. " & nxtNum & vbCrLf)
nxtNum += 1
Next
Finally
ListBox1.EndUpdate()
End Try
nxtNum = 1
Dim items() As Object = ListBox1.Items.Cast(Of Object).Reverse.ToArray
ListBox1.Items.Clear()
ListBox1.Items.AddRange(items)
The example you provided in your comment should work if not for one problem: the Step -1
The For-loop works like this when performing a new iteration:
New Value = Current Value + Step
Meaning, when you do:
For i As Integer = 0 To ListBox1.Items.Count - 1 Step -1
you keep doing Current Index + (-1), which is why they end up in the top; you keep going one step back.
The simple way to fix this is to change it to Step 1 or just remove the Step part completely (as the default step is 1, if omitted).
For i As Integer = 0 To ListBox1.Items.Count - 1
...your code...
Next
EDIT:
I think I see your actual problem, I missed it... The descriptions are already there when you add these headers.
In this case you should be able to do:
For i As Integer = 0 To ListBox1.Items.Count - 1 Step 2
...your code...
Next

How to delete empty datagridview cells on import

Currently I have my program hiding blank or empty datagridview cells. I want to find a way to delete these cells entirely. The reason being, after the blank cells were hidden they would reappear after going through some of my other validations. These validations checked to see if the cells contained any invalid input such as negative numbers,non-numeric input and blank cells. If they contained any of the above they would be populated with default values, thus making my hidden cells reappear. Hopefully if there is a way to delete these cells they won't have a change of getting filled with default data. I've found the below code on MSDN but it doens't seem to work properly for whatever reason. Also I'm using the DATABINDINGCOMPLETE event. I'm not sure if there is another event that would work better for this situation. I greatly appreciate any help you may give!
Private Sub DataGridView1_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete
Dim i As Integer = 0
Dim mtCell As Integer = 0
For Each row As DataGridViewRow In DataGridView1.Rows
For j = 1 To row.Cells.Count -2
If row.Cells(j).Value Is DBNull.Value Then
mtCell += 1
End If
Next
If mtCell = row.Cells.Count Then
DataGridView1.Rows.RemoveAt(i)
End If
i += 1
mtCell = 0
Next
end sub
There are various problems with your code. Here you have an improved version which should work without any problem:
Dim mtCell As Integer = 0
Dim row As DataGridViewRow = New DataGridViewRow()
For rowNo As Integer = DataGridView1.Rows.Count - 2 To 0 Step -1
row = DataGridView1.Rows(rowNo)
Try
For j = 0 To row.Cells.Count - 2
If row.Cells(j).Value Is Nothing OrElse row.Cells(j).Value Is DBNull.Value Then
mtCell += 1
End If
Next
If mtCell = row.Cells.Count - 1 Then 'I understand that you want to delete the row ONLY if all its cells are null
DataGridView1.Rows.RemoveAt(rowNo)
End If
mtCell = 0
Catch ex As Exception
Exit For
End Try
Next rowNo
First thing, it is better to iterate the Collection "backwards" when deleting in order to avoid problems (example: 3 rows; you delete the first position and the loop goes to the second one; but after the deletion all the rows "move up" and thus the second position is now occupied by the third row -> you would skip the second row and, eventually, iterate beyond the limits of the Collection). DBNull.Value is quite restrictive; not sure if it is working fine under your specific conditions, but better complementing it with Nothing. You cannot affect the item being iterated in a For Each loop (unlikely in a normal For one); in this case you are affecting it indirectly but just to make sure, better relying on a normal For loop. You are iterating through rows but you are not deleting these rows, but the ones defined by a counter (i) which is not necessarily related to the current row number, better getting rid of it. Lastly I have included a try...catch to make sure (that you don't access an inexistent position).

Error code using Linq to add to a variable

I'll start with the problem, and then describe how the procedure works.
I'm getting a an error saying:
Value of type 'System.Collections.Generic.IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group)' cannot be converted to 'LunchMoneyGame.LunchMoneyMainForm.Group'.
The blue underline pinpointing the problem is on Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = From r In temp Where r.ID = Number Select r on the right side of the operator.
I know it is something to do with explicitness
--
This is for a table top card game i am converting to a text game.
The goal of this sub procedure is to subtract a card from a players hand and add it to the discard pile. Once added to the discard pile I then call the discard pile to move the cards from the discard pile to the card deck.
The problem starts with the number generator.
What I'm doing is randomly picking a card from the deck. by generating a the card id of a card in the deck. Once the ID is generated using linq the number generated then takes a card, then adds the card to the players inventory (quantity integer), and subtracts from the deck.
T represents whose turn it is. Currently for testing purposes I have coded it for only 2 players in terms of the T variable.
Private Sub GrabFromDeckAndDiscard()
Dim CardCheckBoxInteger As Integer
'ReDeclare CheckBox Array for Private sub
Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
'Discard
Select Case T
Case 0
Player1HandGroup(NumberArray(Checks)).QuantityInteger -= 1
Case 1
Player1HandGroup(NumberArray(Checks)).QuantityInteger2 -= 1
Case 2
Player1HandGroup(NumberArray(Checks)).QuantityInteger3 -= 1
Case 3
Player1HandGroup(NumberArray(Checks)).QuantityInteger4 -= 1
Case 4
Player1HandGroup(NumberArray(Checks)).QuantityInteger5 -= 1
End Select
'Add Card to Discard Pile
DiscardGroup(NumberArray(Checks)).QuantityInteger += 1
'Shuffle Deck from Discard Pile if Deck is out of cards
Call DiscardPile()
'Reset Number Generator, unless weapon isn't discard
Dim temp As IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group) = From r In DeckGroup Where r.QuantityInteger > 0 Select r
If temp IsNot Nothing AndAlso temp.count > 0 Then
Number = (temp(Rnd.Next(0, temp.Count)).ID)
' ** Edit **: This will give you the actual object to be manipulated
Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = (From r In temp Where r.ID = Number Select r).Single
Dim PlayerQuantitySubtractionInteger As Integer
For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
' ** Edit **
obj.QuantityInteger -= 1
'Select the Player depending value of T
Select Case T
Case 0
Player1HandGroup(Number).QuantityInteger += 1
Case 1
Player1HandGroup(Number).QuantityInteger2 += 1
Case 2
Player1HandGroup(Number).QuantityInteger3 += 1
Case 3
Player1HandGroup(Number).QuantityInteger4 += 1
Case 4
Player1HandGroup(Number).QuantityInteger5 += 1
End Select
CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
NumberArray(PlayerQuantitySubtractionInteger) = Number
Next PlayerQuantitySubtractionInteger
End If
'Switch to next player
Select Case T
Case 0
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Text = Player1HandGroup(NumberArray(CardCheckBoxInteger + 5)).CardNameString
Next
T += 1
Case 1
If GameSize = 2 Then
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Text = Player1HandGroup(NumberArray(CardCheckBoxInteger)).CardNameString
Next CardCheckBoxInteger
T -= 1
End If
If GameSize > 2 Then
T += 1
End If
Case 2
Case 3
Case 4
End Select
Label1.Text = T.ToString
'Clear Check Boxes when turn is finished
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Checked = False
Next
'Turn off play button
PlayButton.Enabled = False
End Sub
From r In temp
Where r.ID = Number
Select r
Returns a collection of everything which matches the Where clause (see this for the MSDN documentation). .NET represents this as an IEnumerable, which means a collection of things which can be iterated over (e.g. using a For loop or something like that).
There is no automatic concept that you'll only get one record returned even though your condition is an = (there may also be conditions, broadly, where no elements are returned such as when nothing matches the equality condition or where multiple things match the equality condition).
In order to indicate that you only want one element you can use the First, Single, FirstOrDefault or SingleOrDefault methods.
For example:
Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = (From r In temp Where r.ID = Number Select r).Single()
Which will give you an object of type LunchMoneyGame.LunchMoneyMainForm.Group, which I think you're looking for in this case.
As an aside, it's not bad to know the difference between the methods I've outlined above. In my lay perspective (as I'm no expert):
First returns the first item in the collection or throws an Exception if the collection is empty
FirstOrDefault returns the first item in the collection or null if empty
Single returns the only item in the collection or throws an Exception if the collection does not have exactly one item
SingleOrDefault returns the first item in the collection, null if the collection is empty and throws an exception if the collection has more than 1 element
So in your case, since you should only match on a single item with the = condition I would suggest using Single or SingleOrDefault
PS. Sorry, when I say null above, that'll be Nothing in VB.NET speak