I have a DataSet with several DataTables inside. I'm displaying the DataTables in a ListView (I didn't know about databinding when I wrote the code). Anyway, I would like to remove rows from the DataTables inside the DataSet.
I have tried this:
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
I've also tried
dsData.Tables["Table1".Rows.Delete(row);
The problem I'm experiencing is that the when you remove a row I get the exception:
Collection was modified; enumeration operation might not execute.
From what I understand it's because when you remove a row from a ListView the row below it moves up and causes all this trouble. The code itself does what it's supposed to but it's not nice to see that exception when when you run it.
I was about to rewrite the whole class with a DataGridView but would rather correct that single line if possible :).
EDIT: I'm not even sure a DataGridView would solve the problem anyway.
change the loop to a for loop counting backwards so you don't get that message.
for(int i = dsData.Tables["TAble1"].Rows; i > 0; i--)
{
if(item.SubItems[2].Text == dsData.Tables["Table1"].Rows[i - 1]["LoginName"].ToString())
dsData.Tables["Table1"].Rows.Remove(i - 1)
}
Try:
DataSet dsData = new DataSet();
List<DataRow> rowsToDelete = new List<DataRow>();
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
rowsToDelete.Add(row);
}
}
foreach(DataRow row in rowsToDelete)
{
dsData.Tables["Table1"].Rows.Remove(row);
}
You need a backwards for loop if you're going to be removing things
(explanation of why here)
for (int i = dsData.Tables["Table1"].Rows.Count - 1; i >= 0; i--)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
in general you can't remove items from a collection within a loop that is iterating on it
what you could do is keep a list of all the row you want to remove (creating it within the loop) and remove all of them OUTSIDE the loop
You can't modify a collection that you are iterating though with a foreach loop, from inside the loop. Do this instead:
for (int i = 0; i < dsData.Tables["Table1"].Rows.Count; i++)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
Related
I have a datagridview which store a path to files with extensions and I must delete rows which are .exe extensions.
My Code:
foreach (DataGridViewRow row in dataGridViewgrida.Rows)
{
if (row.Cells[1].Value.ToString().Contains(".exe"))
row.Cells[1].Style.ForeColor = Color.Red;
dataGridViewgrida.Rows.RemoveAt(this.dataGridViewgrida.Rows.); // ?? I do not know how to do this
}
You should use reverse loop and delete row based on index:
for(int i=dataGridViewgrida.Rows.Count - 1; i>=0; i--)
{
var row = dataGridViewgrida.Rows[i];
if (row.Cells[1].Value.ToString().Contains(".exe"))
{
dataGridViewgrida.Rows.RemoveAt(i); // or dataGridViewgrida.Rows.Remove(row );
}
}
It is a bad idea to remove an item from a collection you're currently iterating over. Put the current row into a temporary collection and remove them after your foreach loop.
You cannot remove items from the collecion when you are looping it. Create a list to keep the rows you need to delete a populate it inside the loop.
Then you can remove them in another loop:
foreach(DataGridRow row in yourList)
dataGridViewgrida.Rows.Remove(item);
If I execute the following code fragment:
IXLRange dataRange = worksheet.Range(1, 1, myData.Count, 4);
foreach (var row in dataRange.Rows()) {
//int cells = row.CellCount();
if (isEndOfGroup) {
row.InsertRowsBelow(1);
var rowBelow = row.RowBelow();
// Do stuff with the added row, like adding a subtotal
}
}
What's actually happening here? It seems like the row collection being iterated on is being updated, which is what I want to happen, because the commented-out line throws an exception with the error "Range is invalid" on the next iteration, as if the row somehow hasn't been initialised yet.
I understand modifying a collection as you iterate through it is bad practice, but I've done this before without any issues.
What would be an alternative way of achieving this?
Never modify a collection while iterating... It is a bid.
Take advantage of the for loop.
When it did not raise an exception before, then you just changed the content of the row but not the reference of to row itself.
I ended up using a for loop as Bagerfahrer suggested and having a second counter to keep track of the row number:
int rowNum = 1; // Row numbering starts at 1
for (int i = 0; i < myData.Count; i++) {
var row = worksheet.Row(rowNum);
if (isEndOfGroup) {
row.InsertRowsBelow(1);
rowNum++;
var rowBelow = row.RowBelow();
// Do stuff with the added row, like adding a subtotal
}
rowNum++;
}
This allows me to index into myData for retrieving data and also keep track of where I'm writing to in the spreadsheet.
Here is the code I am using.
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
lst = lvw.Items.Add(row(0)); // My error is on row
for (int i = 1; i <= data.Columns.Count - 1; i++)
{
lst.SubItems.Add(row(i));
}
}
Now, what I'm doing or trying to do is actually add database items onto my listview. However, I can't seem to get my rows onto my list view. I get a syntax error:
'Rows' is a 'variable' but is used like a 'method'.
I have been struggling with this winform for a couple days and my coding has stopped dead at this point. I have tried changing the parenthesis. Here is the whole function. I got it from the internet in vb.net and ran the code it worked. I then configured that code for my application in vb.net that worked too, now after converting to c# and it doesn't work which is my problem. I went through all the code(I am new to c# like relatively) and fixed most of it to work for my application except the row error
private void ShowDataInLvw(DataTable data, ListView lvw)
{
lvw.View = View.Details;
lvw.GridLines = true;
lvw.Columns.Clear();
lvw.Items.Clear();
foreach (DataColumn col in data.Columns)
{
lvw.Columns.Add(col.ToString());
}
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
lst = lvw.Items.Add(row(0));
for (int i = 1; i <= data.Columns.Count - 1; i++)
{
lst.SubItems.Add(row(i));
}
}
}
In C#, to get to an index of a collection, you need to use [ ] (whereas VB uses ( )). Refer to this specifically for DataRows.
Thus, your code should look like:
lst = lvw.Items.Add(row[0]);
And
lst.SubItems.Add(row[i])
On a side note, you may want to read up on the use of the default keyword.
EDIT:
Here's a complete listing (without spoon-feeding much), after noticing that your code won't actually work and assuming you really intend to do as described:
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
for (int i = 0; i <= data.Columns.Count - 1; i++)
{
if (i == 0)
lst = new ListViewItem(row[0].ToString());
else
lst.SubItems.Add(row[i].ToString());
}
lvw.Items.Add(lst);
}
Notice a few things:
The instantiation of lst has been changed.
The lvw will add the lst along with its subitems.
The addition will occur only after the subitems have been created.
Disclaimer: I haven't tested the code, but the idea is there.
Have you tried:?
lst = lvw.Items.Add(row[0]);
lst.SubItems.Add(row[I]);
I've gone through the other answers for this kind of question, nothing has really worked well so far.
I have a foreach loop that should add the the row from the source datagridview to the destination datagridview then remove that row from the source.
The invalid operation exception is: Row provided already belongs to a DataGridView control.
I couldn't get ...Rows.Copy() to work either. Any ideas?
foreach (DataGridViewRow selRow in fromDataGridView.SelectedRows)
{
toDataGridView.Rows.Add(selRow);
fromDataGridView.Rows.Remove(selRow);
}
You need to remove the row from fromDataGridView before you add it to toDataGridView.
But you're modifying the collection inside the foreach loop - that won't work.
The workaround is to copy the collection for use in the foreach.
Try this:
foreach (DataGridViewRow selRow in fromDataGridView.SelectedRows.OfType<DataGridViewRow>().ToArray())
{
fromDataGridView.Rows.Remove(selRow);
toDataGridView.Rows.Add(selRow);
}
Here is an example of what you could do or try..
its happening when one row is removed the rows count decrements too so if you put your code in for loop and run it in reverse it would work fine have a look:
for (int selRow = dataGridView1.Rows.Count -1; selRow >= 0 ; selRow--)
{
toDataGridView.Rows.Add(selRow);
fromDataGridView.Rows.Remove(selRow);
}
Adding a new row directly to DataGridView is not possible when there is BindingSource.
You should add row to the BindingSource of second view and let it to add the row to its grid.
I've tested the below code in a working solution.
var selectedRows = dataGridView1.SelectedRows;
int count = dataGridView1.SelectedRows.Count;
for (int i = 0; i < count; i++)
{
bindingSource2.Add(bindingSource1[selectedRows[i].Index]);
dataGridView1.Rows.Remove(selectedRows[i]);
}
In my below code I'm comparing values (email addresses) in my Arrarylist (called emails) with values in my Datatable (called dtResult). I was hoping to be able to remove rows from the datatable if it has an email address that is found in the arraylist but I'm starting to think I'm better off creating a new datatable that does not have those rows instead (I just don't know how to do that). Obviously my code below bombs after a row is deleted because it loses its place in the foreach loop. How can I fix this?
Thanks.
foreach (DataRow row in dtResult.Rows)
{
var tmpl = row[3];
for (int x = 0; x < emails.Count; x++) //emails is an ArrayList of email addresses
{
string one = emails[x].ToString().ToUpper();
string two = tmpl.ToString().ToUpper();
//compare datatable value to arraylist value..
if (one == two)
{
//if they are equal then I want to remove them (or maybe create a new datatable??)
dtResult.Rows.Remove(row);
dtResult.AcceptChanges();
}
}
}
The easiest way would be to loop through the DataTable backward:
for (int i = dtResult.Rows.Count - 1; i >= 0; i--)
{
var row = dtResult.Rows[i];
// ...
}
You also might want to consider whether you can just mark the row state on those rows as deleted (and then observe that status later) instead of actually removing them from the table.
You could go through the DataTable in reverse
for(int i = dtResult.Rows.Count; i >= 0; i--)
{
foreach(object email in emails)
{
if(email.ToString().ToUpper() == dtResult[i].ToString().ToUpper())
{
dtResult.Rows.Remove(dtResult.Rows[i]);
dtResult.AcceptChanges();
}
}
}
Why don't you do it in two steps?
I didn't try this, so let me know how it goes.
var deleteRows = new List<DataRow>();
then in your loop, instead of deleting the row right away, just add the row to your list:
deleteRows.Add(row);
and after you loop through your database, you loop through the deleteRows list and delete each row one by one.