DataGridView Remove Error - vb.net

I want to delete the duplicate value and blank lines in the DataGridView.
I am using the following code for this.
Private Sub dgwsil()
On Error Resume Next
For i2 As Integer = DataGridView1.RowCount - 1 To 0 Step -1
If Trim(DataGridView1.Rows(i2).Cells(0).Value.ToString()) = "" Then
DataGridView1.Rows.RemoveAt(i2)
End If
Next
Dim numberOfRows = DataGridView1.Rows.Count
Dim i As Integer = 0
While i < numberOfRows - 2
For ii As Integer = (numberOfRows - 1) To (i + 1) Step -1
If DataGridView1.Rows(i).Cells(0).Value.ToString() = DataGridView1.Rows(ii).Cells(0).Value.ToString() Then
DataGridView1.Rows.Remove(DataGridView1.Rows(ii))
numberOfRows -= 1
End If
Next
i += 1
End While
End Sub
Code sometimes works fine.
But sometimes it gives an error
How can I solve this problem ? Or is there any code you use for it?

It looks like you are going into this problem with a VBA mindset. In VBA, you would usually manipulate the datagridview directly and the On Error Resume Next line would be acceptable.
In VB.NET you don't want to do either of those things. Your datagridview should be bound to a collection of information. That collection can be a datatable or an array or any type of collection really. So instead of using a loop to manipulate the datagridview directly you would either loop through the backing collection or better yet LINQ through the collection and manipulate it however you need. There are a ton of tutorials out there explaining everything you could ever need so googling around for how to bind a datatable to a datagridview should get you what you need.
I would also suggest looking into the Try...Catch...Finally... structure. You should really use that to handle any errors that could possibly come up from your code.

Related

Visual Studio (List.Count - 1) = List.Count

I've been trying around for a bit now and I'm totally confused why my program behaves this way.
I tried to search on google and here but maybe I just have trouble phrasing my question right or I'm the only one with this problem.
Anyways, so here's my problem:
Protected myList as List(Of CustomClass) = new List(Of CustomClass)
Public Sub mySub()
Dim ListCount as Integer = myList.Count
ListCount = ListCount - 1
For i As Integer = 0 To ListCount Step 1
If myList(i).MyStatus = FMyStatus Then
myList(i).MyFunction.Invoke()
myList.RemoveAt(i)
End If
Next
End Sub
This throws me an exception all the time that i is larger than the myList-index.
The problem is that ListCount even though I reduced it by 1 still stays the same, it's not possible to reduce it by 1.
But if I for example reduce it by 2, it's totally fine and does it stuff.
Is this common? Did I miss on something? I already tried to find something on the msdn website for the List.Count Property but wasn't able to find something that explains me why I can't reduce the value by 1.
Hope you guys can enlighten me.
Cheers.
You need to loop backwards to accomplish this:
For i As Integer = myList.Count - 1 To 0 Step -1
If you go the other way, when you remove an item, the list becomes smaller, but you are still trying to go to the end of the original list.

vb.net getting a collection of rows from a bound datagridview based on selection

I've read around and googled but didn't find the little thing which would help me. I think I've got some simple logical error.
Ok, so here is what I have:
A DataGridView bound to a Datatable populated with filenames (and other info, mainly audio format).
Well, now I've written a sub (VS2013 and using .Net 4.0) for printing which works fine, I tried to add an option to print only selected rows from the DataGridView and thought ok, then I give the printing routine the whole rows of the DataGridView or only the selected rows based on that option.
This is what I tried:
On the form Level I defined the rowCollection as:
Dim rowCollection As DataGridViewRowCollection
The BeginPrint Sub then has the following lines to get the rows:
'*** Get Rows to print
rowCollection = New DataGridViewRowCollection(FileDataGridView)
If My.Settings.PrintSelected And (FileDataGridView.SelectedRows.Count > 0) Then
Dim row As New DataGridViewRow
For r = FileDataGridView.SelectedRows.Count - 1 To 0 Step -1
row = FileDataGridView.SelectedRows(r)
rowCollection.Add(FileDataGridView.Rows(row.Index))
Next
Else
rowCollection = FileDataGridView.Rows
End If
I thought a rowCollection would be something like an array of the single rows,
but apparently this doesn't do the job.
If I want to print all rows, this works fine, but for the selected rows, I get an error the no elements could be added to the row collection because the DataGridView is databound. I thought the rowCollection could be population by single rows, an not only link to the whole DataGridView. Where am I wrong?
I also tried to get only the .SelectedRows as a collection but then I get type errors as DataGridView.Rows and DataGridView.SelectedRows do not have the same format?
I would like to get a list/collection/array of the rows or selected rows, which I then can use for printing.
Any help would be appreciated
regards,
Christian
Surely not a perfect solution but does the job:
Dim rows As List(Of DataGridViewRow)
Dim rowarray(DataGridView1.Rows.Count) As DataGridViewRow
If DataGridView1.SelectedRows.Count > 0 Then
DataGridView1.SelectedRows.CopyTo(rowarray, 0)
Else
DataGridView1.Rows.CopyTo(rowarray, 0)
End If
rows = rowarray.ToList
rows.RemoveAll(Function(a As DataGridViewRow) a Is Nothing)
There is a difference between a SelectedRowCollection and a RowCollection, for what ever reason they didn't used inheritence for those 2, so you need to cheat a little to process both the same way

VB.NET Is there a quicker way to scan through a RichTextBox?

I have a VB.NET application that I use to load various files into a RichTextBox and then scan through the document to find specific words. It's similar to the Find function in Word. The app was running fine until a 5,150 line .sql document run through it and it's taking upwards of 10 minutes to run to completion.
Can anyone recommend a better way of coding it than I have below?
If sqlText.Contains("GRANT") Then
Dim searchstring As String = "GRANT"
Dim count As New List(Of Integer)()
For i As Integer = 0 To rtbFile.Text.Length - 1
If rtbFile.Text.IndexOf(searchstring, i) <> -1 Then
count.Add(rtbFile.Text.IndexOf(searchstring, i))
End If
Next
Try
For i As Integer = 0 To count.Count - 1
rtbFile.Select(count(i), searchstring.Length)
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
count.RemoveAt(i)
Next
Catch ex As Exception
End Try
rtbFile.Select(rtbFile.Text.Length, 0)
rtbFile.SelectionBackColor = Color.White
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Regular)
End If
That first loop is killing the performance, you are calling IndexOf for every character in the string. Also the two loops can be merged in to one. Change it to:
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
For Each m As Match in Regex.Matches(sertbFile.Text, searchstring)
rtbFile.Select(m.Index, searchstring.Length)
Next
This can also be done with a While loop and RichTextBox.Find():
Dim searchstring As String = "GRANT"
Dim index As Integer = rtbFile.Find(searchstring, 0, RichTextBoxFinds.None)
While index <> -1
rtbFile.Select(index, searchstring.Length)
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
index = rtbFile.Find(searchstring, index + searchstring.Length, RichTextBoxFinds.None)
End While
You've got a few bad things going on here:
First, the following code:
For i As Integer = 0 To rtbFile.Text.Length - 1
If rtbFile.Text.IndexOf(searchstring, i) <> -1 Then
count.Add(rtbFile.Text.IndexOf(searchstring, i))
End If
Next
This is looping through every character in your string, and calling IndexOf on the entire string from that point forward. So your 50,000-character string is running IndexOf 50,000 times, on large strings.
You only need to call IndexOf as many times as you find a string. When your string is found, you increment your start index to that point, and keep searching only from that point.
Next thing, this code:
For i As Integer = 0 To count.Count - 1
...
count.RemoveAt(i)
Next
The RemoveAt line is unnecessary. You're already looping through a list, so you don't need to remove the items as you go along. The way it stands, your loop will skip every other item in your list.
Whoops. I missed a very important point about the IndexOf (and incorrectly assumed it was fed with the end of the last match). See Magnus's answer.
I am not sure where the bottleneck is (and it might very well be from setting the selection itself), but here are my suggestions, roughly in order of priority:
Invoke rtbFile.Text once to avoid any roundtrips to underlying control (perhaps a native Windows control?) and use a variable to store the resulting string. Once the string is obtained in .NET, just keep using it directly unless/until the text may change. If the control is native then a lot of work may be required to simply "get the text".
Use normal item iteration over the count collection (not indexing) and do not remove from the front-of the List when assigning the selections. Removing from the front of a List is "expensive" in that it must shift all items down internally. Also, removing the element is unneeded here and dubious at best: since the collection being modified is also is being iterated which likely leads to incorrect behavior (skipped items), regardless of performance.
Only call IndexOf once per loop and use a variable to avoid a duplicate search. This likely won't have an overall impact, but it does avoid some "extra" work. IndexOf itself is fine and doesn't need to be replaced.
YMMV.

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)

How can I rewrite this Visual Basic.net loop to make it work?

I have a For Each loop written in Visual Basic that's trying to modify a table it is iterating through. This is causing the ""Collection was modified; enumeration operation might not execute" exception.
What is frastrating about this is that I've tried to make copies of all the objects that I was using and and only removed from the copy but the exception still happened.
I know that this is due to my using a For Each loop instead of a For or While loop but I could not rewrite my code and make it work (I'm more familiar with C#). This is why I decided to ask for help here.
This is my code. How can I rewrite to be able to remove the Row that I wish to remove? Any help and code would be very appreciated!
Dim drAcademicRecord, drSchool As DataRow
For Each drSchool In dsR.Tables("School").Rows
If drSchool("OrganizationName") = "NA" Then
For Each drAcademicRecord In dsR.Tables("AcademicRecord").Rows
If drAcademicRecord("AcademicRecord_Id") = drSchool("AcademicRecord_Id") Then
dsR.Tables("AcademicRecord").Rows.Remove(drAcademicRecord) '<-- This is the line causing my exception
End If
Next
End If
Next
For i as integer = dsR.Tables("AcademicRecord").Rows.Count - 1 to 0 Step -1
If dsR.Tables("AcademicRecord").Rows(i)("AcademicRecord_Id") = drSchool("AcademicRecord_Id") Then
dsR.Tables("AcademicRecord").Rows.RemoveAt(i)
End If
Next
You don't need to use
dsR.Tables("AcademicRecord").Rows.Remove(drAcademicRecord)
Because you have already got drAcademicRecord, so you can directly delete that row by
drAcademicRecord.Delete
e.g.
For Each drSchool As DataRow In dsR.Tables("School").Select("OrganizationName='NA'")
For Each drAcademicRecord As DataRow In dsR.Tables("AcademicRecord").Select("AcademicRecord_Id=" & drSchool("AcademicRecord_Id"))
drAcademicRecord.Delete
Next
Next