WSC
WSC

Reputation: 992

List of currently displayed rows in DataGridView

I have a List(Of String) which I want to contain a list of primary keys based on if the associated row in dgv is displayed.

I need to both add and remove values from the list as they are displayed/hidden.

Currently I am doing this:

Private Sub dgv_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles dgv.RowStateChanged

    If e.StateChanged = DataGridViewElementStates.Displayed Then
        If Not VisibleRows.Contains(e.Row.Cells("SQ").Value.ToString) Then
           VisibleRows.Add(e.Row.Cells("SQ").Value.ToString)
        End If
    End If

End Sub

However this will just add an item to my list when a new row is displayed without removing the hidden row's primary keys.

I can remove a value from the list using VisibleRows.Remove(e.Row.Cells("SQ").Value.ToString) however I don't know how to identify that a row is no longer displayed.

What is the result of e.StateChanged when a row is no longer displayed?

Upvotes: 0

Views: 537

Answers (1)

Bart Hofland
Bart Hofland

Reputation: 3905

Hmmm.

DataGridViewElementStates is an enum containing flags. You might want to check for it like this:

If e.StateChanged And DataGridViewElementStates.Displayed = DataGridViewElementStates.Displayed Then
    ...
End If

I don't know if the event gets triggered for rows that become invisible. But then again, I would not want to keep track of such a list of strings. Smells not so well.

Personally (if I really need a list of strings containing the visible items), I would do the following:

  • fill the list only when I need it (for example by using a readonly property that refreshes the list if it is invalid).
  • invalidate (or simply dispose of) the list in the dgv_RowStateChanged event handler (or perhaps in a more appropriate event handler; I would need to check)

Something like this:

Private _visibleRows As List(Of String)  'Only use this inside the VisibleRows property and the dgv_RowStateChanged event handler. For all other usage of the list, use the VisibleRows property!

Private ReadOnly Property VisibleRows As List(Of String)
    Get
        If _visibleRows Is Nothing Then
            _visibleRows = New List(Of String)

            For Each row As DataGridViewRow In dgv.Rows
                If row.Displayed Then
                    _visibleRows.Add(row.Cells("SQ").Value.ToString)
                End If
            Next
        End If

        Return _visibleRows
    End Get
End Property

Private Sub dgv_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles dgv.RowStateChanged
    _visibleRows = Nothing
End Sub

But it still does not smell right. Depending on the rest of your code, this might also have a dramatically bad performance.

Edit:

You might replace the For-loop in the VisibleRows property with the following code:

Dim index As Integer = dgv.FirstDisplayedScrollingRowIndex
Dim row = dgv.Rows(index)

While (row.Displayed)
    _visibleRows.Add(row.Cells("SQ").Value.ToString)
    index += 1
    row = dgv.Rows(index)
End While

This might be faster...

Edit 2:

The code in my first edit has a bug: you might get an index-out-of-range-exception when you scroll down to the bottom of the datagrid. So you also need to check inside the While-loop if the increased index value is valid before you try to fetch that next row or exit the loop otherwise. But I'm not going to correct this. I don't like this entire "solution" at all. ;)

Upvotes: 1

Related Questions