Matt Kent Matt Kent - 7 months ago 135
Vb.net Question

DataGridView - Index was out of range.

Before anyone classes this as a duplicate, I have indeed searched both around StackOverflow and the Google for the solution to my problem, neither have proven to be successful.

To summarise: I am building a game as a part of a unit in my CS course "Object Oriented Programming". The aim of the game is to randomly generate planes, who's fuel level deducts using a timer and then events such as crash are fired upon the fuel reaching 0. We then have to land the planes and assign them runways, and so on...

This all works fine, however, I am at the stage where I have to list all the planes that are being generated and then give the user the option to land them.

I initially used a ListView but quickly switched to a DataGridView because I wanted to have a button as a column.

So, intially as the planes were being generated, and furthermore displayed within the DataGridView, whenever I clicked the "Land Plane" button within a row of my choosing the selection would simply jump back to the first row. I asked my lecturer for assitance, and he stated that it's because the fuel's value is constantly being updated as it counts down. When the DataGridView is cleared, the selected row resets.

To resolve this, we added two local variables to store the selected row and the selected column. We checked to see if the selected column still remained within the collection, when the "Land Plane" button was clicked. This is because when the fuel reaches 0 it simply removes the row from the DataGridView.

The problem I am now having, which my lecturer was puzzled by was this:


An unhandled exception of type 'System.ArgumentOutOfRangeException'
occurred in mscorlib.dll

Additional information: Index was out of range. Must be non-negative
and less than the size of the collection.


The code that handles the DataGridView is as follows:

Public Sub populateDataGV()
Dim p As New Aircraft

selRow = -1
'Populate the data grid view
If Not (IsNothing(DataGridView1.CurrentCell)) Then
selRow = DataGridView1.CurrentCell.RowIndex
selCol = DataGridView1.CurrentCell.ColumnIndex
End If


DataGridView1.Rows.Clear()
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Flight No"
DataGridView1.Columns(1).Name = "Fuel"

For Each p In airport.planeCollection
Dim row As String() = New String() {p.name, p.getFuelLevel()}
DataGridView1.Rows.Add(row)
Next

Dim RowsToDelete As New List(Of DataGridViewRow)()
For Each rows As DataGridViewRow In DataGridView1.Rows
If rows.Cells(1).Value IsNot Nothing AndAlso rows.Cells(1).Value.ToString = "0" Then
RowsToDelete.Add(rows)
If selRow = rows.Index Then
selRow = -1
End If
End If
Next
For Each rows As DataGridViewRow In RowsToDelete
DataGridView1.Rows.Remove(rows)
Next
RowsToDelete.Clear()

If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If


'Add button column
Dim btn As DataGridViewButtonColumn = New DataGridViewButtonColumn()
btn.HeaderText = "Action"
btn.Text = "Land Plane"
btn.UseColumnTextForButtonValue = True
DataGridView1.Columns.Add(btn)


End Sub


This is where the error is thrown:

If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If


If anyone can shed some light on what is actually causing this error I'd be most grateful.

Answer

Your range check is open to errors.

First, Your current code allows selRow to be less than -2:

If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
    DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If

Second, you do not need to set the column... instead, just set it to 0 or 1.

You should change

If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
    DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If

to

If selRow >= 0 And selRow <= DataGridView1.Rows.Count - 1 Then
         DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(1)
End If

This new code properly range checks both values.


Also, you need to remove from the top:

selRow = -1

Finally, to address the problem of selecting the right row, I suggest changing:

DataGridView1.Rows.Clear()
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Flight No"
DataGridView1.Columns(1).Name = "Fuel"

For Each p In airport.planeCollection
    Dim row As String() = New String() {p.name, p.getFuelLevel()}
    DataGridView1.Rows.Add(row)
Next

to:

 ' DataGridView1.Rows.Clear()
    If DataGridView1.ColumnCount = 0 Then
        DataGridView1.ColumnCount = 2
        DataGridView1.Columns(0).Name = "Flight No"
        DataGridView1.Columns(1).Name = "Fuel"
    End If

    For Each p In airport.planeCollection
        Dim updated As Boolean = False
        For Each rows As DataGridViewRow In DataGridView1.Rows
            If rows.Cells(0).Value = p.name Then
                rows.Cells(1).Value = p.getFuelLevel
                updated = True
                Exit For
            End If
        Next
        If Not updated Then
            Dim row As String() = New String() {p.name, p.getFuelLevel()}
            DataGridView1.Rows.Add(row)
        End If
    Next

You should add/update rather than simply clearing and adding.