Listbox Insert Issue - vb.net

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

Related

InvalidArgument=Value of ' ' is not valid for 'index' (' ' inside number)

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.

How Do I Count When Numbers Do Not Show Up

I have numbers from 1 to 25, four numbers will show up daily. I need to put a +1 on each of the four numbers and need to put a -1 on each of the 21 numbers didn't show up.
The four numbers that come up daily will be inputted in four different text boxes. The count being positive or negative needs to go on 25 separate text boxes labeled 1 thru 25.
I have tried "if textbox <> number, then count -= 1" but I get a count of -4 because it doesn't see the number in any of the four text boxes.
I only need a daily count not a textbox count. Sorry I don't have any code started and would greatly appreciate if someone can point me in the right direction. I'm doing this on Visual Studio 2012.
Thank you all for responding. Here is some code I've started but the count is not correct. My four input text boxes are in GroupBox2. Four numbers from 1 to 25 will draw daily like a lottery. The four numbers drawn will have a value of +1 each all others -1. I need to find the age of each number 1 thru 25. If a number has a +3 then that means that number has drawn 3 consecutive days. If a number has a -15 then that means that number has not drawn for the past 15 days.
Dim tb As New TextBox
Dim ctrl As Control
Dim Counter As Integer
For Each ctrl In GroupBox2.Controls
tb = ctrl
If tb.Text = 1 Then
Counter += 1
ElseIf tb.Text <> 1 Then
Counter -= 1
TextBox464.Text = Counter
End If
If tb.Text = 2 Then
Counter += 1
ElseIf tb.Text <> 2 Then
Counter -= 1
TextBox463.Text = Counter
End If
If tb.Text = 3 Then
Counter += 1
ElseIf tb.Text <> 3 Then
Counter -= 1
TextBox462.Text = Counter
End If
If tb.Text = 4 Then
Counter += 1
ElseIf tb.Text <> 4 Then
Counter -= 1
TextBox461.Text = Counter
End If
Next
We will need more information about how your going to approach it to be able to help you further, but as for your problem with this If Textbox <> number Then count -= 1 you can use something like this since your only going to be having numbers on the textboxes If Cint(Textbox.Text) <> number then count -= 1 since your using just Textbox its attempting to evaluate it as a control and not the property that your looking for, you need to read from its .Text Property, However since its evaluated as a String and not an Integer it will throw an error exception, thats why the Cint() is included (This may also be used to convert it to integer Ctype(number, Integer) Not sure if there is an execution speed difference or not, however Cint() is a faster way of writing it.) it will try and convert the String into an Integer and when its converted into an integer it can be evaluated like one using <>. No one is going to write a whole solution out for you, but while you attempt to create it yourself and more information can be added we are more than happy to help you with problems along the way.

Does For Each on List return items in order of addition?

If I add items to a List(Of T) and then use a For Each to iterate over them, are they guaranteed to visited in the same order they were added?
Or for that matter, are they added at indexes related to the adding order? (i.e. is the first added object at index 0, the second at index 1, etc)
e.g: Will the below code be guaranteed to to print the output "0 1 2" from both loops?
Private MyList As New List(Of Integer)
MyList.Add(New Integer(0))
MyList.Add(New Integer(1))
MyList.Add(New Integer(2))
For Each n As Integer In MyList
Console.Write(n.ToString & " ")
Next
For i As Integer = 0 To MyList.Count - 1
Console.Write(MyList(i).ToString & " ")
Next
EDIT: Thanks for pointing out that the first part of this question is answered by In what order does a C# for each loop iterate over a List<T>?
However the second part qualifies the question a little further. The linked question states that yes, the For Each iterates from index 0 to index count - 1. But does the .Add() method always add at the next unused highest index?
Yes, every added element to a List gets a higher index. So first is always 0, second 1 etc.
Foreach will always loop the list from the beginning to the end. If you like to do it from the end, you can for example use FOR loop for that or you can sort the list the way you like (f.e. LINQ).
The easiest way to verify this is simply debuging your code.
Using provided code by you (little edits were made):
Sub Main()
Dim MyList As New List(Of Integer)
MyList.Add(0)
MyList.Add(1)
MyList.Add(2)
For Each n As Integer In MyList
Console.Write(n.ToString & " ")
Next
For i As Integer = 0 To MyList.Count - 1
Console.Write(MyList(i).ToString & " ")
Next
Console.Read()
End Sub
The output will be:
0 1 2 0 1 2
If you'd like to insert an element somewhere in the middle, you can use:
MyList.Insert(position, value)

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).

adapting combination code for larger list

I have the following code to generate combinations of string for a small list and would like to adapt this for a large list of over 300 string words.Can anyone suggest how to alter this code or to use a different method.
Public Class combinations
Public Shared Sub main()
Dim myAnimals As String = "cat dog horse ape hen mouse"
Dim myAnimalCombinations As String() = BuildCombinations(myAnimals)
For Each combination As String In myAnimalCombinations
''//Look on the Output Tab for the results!
Console.WriteLine("(" & combination & ")")
Next combination
Console.ReadLine()
End Sub
Public Shared Function BuildCombinations(ByVal inputString As String) As String()
''//Separate the sentence into useable words.
Dim wordsArray As String() = inputString.Split(" ".ToCharArray)
''//A plase to store the results as we build them
Dim returnArray() As String = New String() {""}
''//The 'combination level' that we're up to
Dim wordDistance As Integer = 1
''//Go through all the combination levels...
For wordDistance = 1 To wordsArray.GetUpperBound(0)
''//Go through all the words at this combination level...
For wordIndex As Integer = 0 To wordsArray.GetUpperBound(0) - wordDistance
''//Get the first word of this combination level
Dim combination As New System.Text.StringBuilder(wordsArray(wordIndex))
''//And all all the remaining words a this combination level
For combinationIndex As Integer = 1 To wordDistance
combination.Append(" " & wordsArray(wordIndex + combinationIndex))
Next combinationIndex
''//Add this combination to the results
returnArray(returnArray.GetUpperBound(0)) = combination.ToString
''//Add a new row to the results, ready for the next combination
ReDim Preserve returnArray(returnArray.GetUpperBound(0) + 1)
Next wordIndex
Next wordDistance
''//Get rid of the last, blank row.
ReDim Preserve returnArray(returnArray.GetUpperBound(0) - 1)
''//Return combinations to the calling method.
Return returnArray
End Function
End Class
'
CHANGES//
For wordDistance = 1 To inputList.Count.ToString / 2
Dim count = inputList.Count.ToString
'Go through all the words at this combination level...
For wordIndex As Integer = 0 To inputList.Count.ToString - wordDistance
'Get the first word of this combination level
combination.Add(inputList.Item(wordIndex))
'And all all the remaining words a this combination level
For combinationIndex As Integer = 1 To wordDistance
combination.Add(" " & inputList.Item(wordIndex + combinationIndex))
Next combinationIndex
'Add this combination to the results
If Not wordsList.Contains(combination) Then
wordsList.Add(combination.ToString)
End If
'Add a new row to the results, ready for the next combination
'ReDim Preserve returnArray(returnArray.GetUpperBound(0) + 1)
Next wordIndex
Next wordDistance
One obvious thing in your code is the usage of ReDim Preserve. That can be quite a slow operation since I think it copies the whole array into a new array every time the size is changed, and since you're doing that inside loops I assume that could be a significant issue.
The simplest way of fixing that is to stop using those kinds of arrays and instead use List with it's Add method.
I want to make sure I understand what you are trying to do first. Your problem seems to be:
Given a list of strings,
Return every possible combination of n items from the list,
where n = 2 to length of list
For example, in a list of 5 strings, you would want all combinations of 2 strings, of 3 strings, of 4 strings, and of 5 strings.
If that is an accurate statement of your problem, there is one glaring issue to point out. The number of items you will be generating is on the order of 2 ^ (length of list). This means that trying to generate all combinations of 300 items will never be fast no matter what. Also, for any but the tiniest of lists, you will need to generate items lazily or you will run out of memory.
If you do not want all combinations of all lengths, you may want to clarify your question to better state your desired goal.