DataGridViewRow Clone

C

Chris Shepherd

Here's the scenario:

I have a form, with an unknown dataset (could be ObjectDataSource,
DataTable, etc.) which populates a DataGridView. I need to cache prior
values from a row on the DataGridView before updating them with current
values from the DB, and then do some specific things if particular
columns have changed. This form may be open across several hours. An
example of what I'd like to do is change the DataGridViewRow's
background colour based on the amount of time until a task is supposed
to occur.

According to the documentation at MSDN regarding the Clone method of
DataGridViewRow:

"Creates an exact copy of this row.
Namespace: System.Windows.Forms
Assembly: System.Windows.Forms (in system.windows.forms.dll)
[... Syntax section snipped ...]
Return Value
An Object that represents the cloned DataGridViewRow.
RemarksRemarks

The Clone method copies the row and its property values, but does not
copy the cell values that the row contains. For information about how to
copy cell values when cloning a row, see the example section.

Override the Clone method whenever you derive from DataGridViewRow and
add new properties to the derived class."


Now, the problem I'm having is that the original row has all the proper
values, and importantly, I can reference the cells by the underlying
table's column name but with the cloned version I cannot. I'm guessing
this is because the cloned row doesn't know about the underlying
bindings. Any suggestions on the workaround, keeping in mind I have no
idea what the underlying DataSource might be?

Simple but functional example:

From the Form1.cs --all that's in the designer files is just a default
new form with a fill-docked DatagGridView named "grid" and the listener
for CellContentClick on said grid. The last line of
grid_CellContentClick is where it breaks with "could not find column
named 'Name'":

public partial class Form1 : Form
{
private DataTable someTable;

public Form1()
{
InitializeComponent();
grid.AllowUserToAddRows = false;

someTable = new DataTable();
someTable.Columns.Add("Name");
someTable.Columns.Add("Email");
someTable.Columns.Add("SomeFlag");

someTable.Rows.Add("Joe Test", "(e-mail address removed)", true);
someTable.Rows.Add("Tim Test", "(e-mail address removed)", false);
someTable.Rows.Add("Ted Test", "(e-mail address removed)", false);
}

private void Form1_Load(object sender, EventArgs e)
{
Console.WriteLine(someTable.Rows.Count.ToString());
grid.DataSource = someTable;
}

private void grid_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
DataGridViewRow newRow = grid.Rows[e.RowIndex];
DataGridViewRow cachedRow = (DataGridViewRow)newRow.Clone();

for (Int32 index = 0; index < newRow.Cells.Count; index++)
cachedRow.Cells[index].Value =
newRow.Cells[index].Value;

newRow.Cells["SomeFlag"].Value = false;

Console.WriteLine("{0}: {1}",
newRow.Cells["Name"].Value.ToString(),
newRow.Cells["SomeFlag"].Value.ToString());
Console.WriteLine("{0}: {1}",
cachedRow.Cells["Name"].Value.ToString(),
cachedRow.Cells["SomeFlag"].Value.ToString());
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Chris,

Why not just cycle through the cells for a row and store the values of
each cell in an array? My assumption is that the schema of the data will
remain the same, so you can easily get the count of the cells, store the
values, and then do a comparison later if need be.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Chris Shepherd said:
Here's the scenario:

I have a form, with an unknown dataset (could be ObjectDataSource,
DataTable, etc.) which populates a DataGridView. I need to cache prior
values from a row on the DataGridView before updating them with current
values from the DB, and then do some specific things if particular columns
have changed. This form may be open across several hours. An example of
what I'd like to do is change the DataGridViewRow's background colour
based on the amount of time until a task is supposed to occur.

According to the documentation at MSDN regarding the Clone method of
DataGridViewRow:

"Creates an exact copy of this row.
Namespace: System.Windows.Forms
Assembly: System.Windows.Forms (in system.windows.forms.dll)
[... Syntax section snipped ...]
Return Value
An Object that represents the cloned DataGridViewRow.
RemarksRemarks

The Clone method copies the row and its property values, but does not copy
the cell values that the row contains. For information about how to copy
cell values when cloning a row, see the example section.

Override the Clone method whenever you derive from DataGridViewRow and add
new properties to the derived class."


Now, the problem I'm having is that the original row has all the proper
values, and importantly, I can reference the cells by the underlying
table's column name but with the cloned version I cannot. I'm guessing
this is because the cloned row doesn't know about the underlying bindings.
Any suggestions on the workaround, keeping in mind I have no idea what the
underlying DataSource might be?

Simple but functional example:

From the Form1.cs --all that's in the designer files is just a default new
form with a fill-docked DatagGridView named "grid" and the listener for
CellContentClick on said grid. The last line of grid_CellContentClick is
where it breaks with "could not find column named 'Name'":

public partial class Form1 : Form
{
private DataTable someTable;

public Form1()
{
InitializeComponent();
grid.AllowUserToAddRows = false;

someTable = new DataTable();
someTable.Columns.Add("Name");
someTable.Columns.Add("Email");
someTable.Columns.Add("SomeFlag");

someTable.Rows.Add("Joe Test", "(e-mail address removed)", true);
someTable.Rows.Add("Tim Test", "(e-mail address removed)", false);
someTable.Rows.Add("Ted Test", "(e-mail address removed)", false);
}

private void Form1_Load(object sender, EventArgs e)
{
Console.WriteLine(someTable.Rows.Count.ToString());
grid.DataSource = someTable;
}

private void grid_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
DataGridViewRow newRow = grid.Rows[e.RowIndex];
DataGridViewRow cachedRow = (DataGridViewRow)newRow.Clone();

for (Int32 index = 0; index < newRow.Cells.Count; index++)
cachedRow.Cells[index].Value =
newRow.Cells[index].Value;

newRow.Cells["SomeFlag"].Value = false;

Console.WriteLine("{0}: {1}",
newRow.Cells["Name"].Value.ToString(),
newRow.Cells["SomeFlag"].Value.ToString());
Console.WriteLine("{0}: {1}",
cachedRow.Cells["Name"].Value.ToString(),
cachedRow.Cells["SomeFlag"].Value.ToString());
}
}
 
C

Chris Shepherd

Nicholas said:
Chris,

Why not just cycle through the cells for a row and store the values of
each cell in an array? My assumption is that the schema of the data will
remain the same, so you can easily get the count of the cells, store the
values, and then do a comparison later if need be.

My idea was to make it somewhat more flexible by allowing other devs to
specify which columns to act upon and how, but I guess there's no way
around it short of just using a Collection or Dictionary list to handle
that.

My thinking on the documentation is that it isn't necessarily clear that
you can't access the data in the clone in the same manner you can access
it in the original. It seems incorrect that this will be an exact copy
of the original row. I understand why this may be the case but
personally I like to leave very little room for confusion or
misinterpretation if possible.


Chris.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top