Vb.net – Cannot remove rows in datagridview in vb.net

datagridviewfilterrowsvb.net

My datagridview is bound to a data table. I want to filter the datagridview without affecting data table. I know one way is to use bindingsource, but binding source will use column names in its filter. But I want to enable users to enter any string in a textbox to filter. So I pass datagridview by reference to a function PassFilter which removes unwanted rows in datagridview. I can see that it does execute rows removal in datagridview. But the problem is that when datagridview is shown on the form, it is unchanged, the same as the data table. I do not understand why rows are not removed on datagridview

The code is as follows:

DataGridView1.DataSource = table

PassFilter(DataGridView1, m_FilterQuick.strFilter.ToLower())
Private Sub PassFilter(ByRef datagridview As DataGridView, ByVal strFilter As String)
    Dim i As Integer = 0
    Dim j As Integer = 0
    Dim containStr As Boolean = False

    While i < datagridview.Rows.Count
        j = 0
        containStr = False
        'If all cells in a row does not contain strFilter, delete this row from datagridview
        While j < datagridview.Rows(i).Cells.Count
            Dim c As DataGridViewCell = datagridview.Rows(i).Cells(j)
            If Not c.Value Is DBNull.Value Or Nothing Then
                If c.Value.ToString().ToLower().Contains(strFilter) Then
                    containStr = True
                    Exit While
                End If

            End If
            j = j + 1
        End While
        If Not containStr Then
            datagridview.Rows.RemoveAt(i)
        End If
        i = i + 1
    End While

End Sub

Best Answer

Your big problem is that you're removing items in the DataGridViewRowCollection as you're iterating through that collection. Even if you're not getting an 'index out of range' error doing this you're going to end up removing the wrong row(s) as the index value of later rows change when you remove earlier rows.

What you should do is instead of removing rows in your row loop, add the row's index to a List(Of Integers).

After you're done iterating through all the rows in your DataGridView, Reverse() your list and use the row index values in your List to remove rows via the RemoveAt() method.

Here's what I mean:

Private Sub PassFilter(ByRef datagridview As DataGridView, ByVal strFilter As String)
    Dim i As Integer = 0
    Dim j As Integer = 0
    Dim containStr As Boolean = False
    ' Row indexes we'll remove later on.
    Dim deleteIndexList As List(Of Integer) = New List(Of Integer)

    While i < datagridview.Rows.Count
        j = 0
        containStr = False
        'If all cells in a row does not contain strFilter, delete this row from datagridview 
        While j < datagridview.Rows(i).Cells.Count
            Dim c As DataGridViewCell = datagridview.Rows(i).Cells(j)
            ' Note: you'll want to enclose the tests for DBNull.Value and Nothing in parens like I show here.
            If Not (c.Value Is DBNull.Value And c.Value Is Nothing) Then
                If c.Value.ToString().ToLower().Contains(strFilter) Then
                    containStr = True
                    Exit While
                End If

            End If
            j = j + 1
        End While
        If Not containStr Then
            ' Don't remove rows here or your row indexes will get out of whack!
            ' datagridview.Rows.RemoveAt(i)
            deleteIndexList.Add(i)
        End If
        i = i + 1
    End While

    ' Remove rows by reversed row index order (highest removed first) to keep the indexes in whack.
    deleteIndexList.Reverse()
    For Each idx As Integer In deleteIndexList
        datagridview.Rows.RemoveAt(idx)
    Next
End Sub
Related Topic