Simon Klaver Simon Klaver - 1 month ago 5x
C# Question

Index out of range error when adding a cloned DataGridViewRow to a DataGridViewRowCollection

I got the error:

Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index

This error happens when I add a cloned row to a
, at the line
. However, it does not happen immediately, as the function gets called a few times first on which it completes successfully.

My code is:

public DataGridViewRowCollection getSelectedRows()
object value = null;
DataGridViewRowCollection collection = new DataGridViewRowCollection(this);
foreach (DataGridViewRow row in Rows)
value = row.Cells[columnCheckBox.Index].Value;
if (value != null && (bool) value == true)
collection.Add(cloneRowWithValues(row)); //error happens here
return collection;

is a class derived from

is defined as:

private DataGridViewRow cloneRowWithValues(DataGridViewRow oldRow)
DataGridViewRow newRow = (DataGridViewRow)oldRow.Clone();
for (Int32 i = 0; i < oldRow.Cells.Count; i++)
newRow.Cells[i].Value = oldRow.Cells[i].Value;
return newRow;

This last function I got from the MSDN documentation.

So my question is: What do I do wrong here, and how can I fix it?

I have added some extra information which came from the debugger.

Debug info

More debug info


The error still remains exactly the same when changing the name of variable
in function
, which I therefore altered to avoid confusion.


Your problem lies in this:

DataGridViewRowCollection collection = new DataGridViewRowCollection(this);

This creates a DGVRowCollection that is linked to (source)

The DataGridView that owns the DataGridViewRowCollection.

Changing this object will change the DataGridView. You seem to not want to change the current DataGridView, but merely to read some data from it.

The way it is now, collection is linked to the same DGV as Rows. Changing a collection while foreach iterating it is a compiler error for most collections. But because you didn't change Rows, but collection, with collection.Add(cloneRowWithValues(row)); this weird construction evaded that, and instead blew up.

Change it to a List<DataGridViewRow> list = new List<DataGridViewRow>() instead, and add those rows that match the condition. Return the list. Or with LINQ, as DGVR is an Enumerable:

var selected = Rows.Where(row => row.Cells[columnCheckBox.Index].Value as bool? == true);

Also, those debug screenshots are useless, what's interesting is the variables at time of crash. (PMing with OP got me better info).

EDIT: And I now read the actual cloning function:

DataGridViewRow newRow = (DataGridViewRow)oldRow.Clone();
for (Int32 i = 0; i < oldRow.Cells.Count; i++)
    newRow.Cells[i].Value = oldRow.Cells[i].Value;
return newRow;

DataGridViewRow is like an array of DataGridViewCell reference objects. Cloning it gives a new Row object with references to the same cells. newRow.Cells[i] is the exact same object as oldRow.Cells[i]. The assignment here does literally nothing, as it stores a value over itself.