Reputation: 1530
I have a button on my Win Form that goes through each row in my DataGridView and calls another function as needed.
The loop and function work, however, it seems to be skipping rows. I selected 6 rows in a row, and only 3 got processed (at the end of the function it removes the row, only 3 of the 6 were removed).
I confirmed that the loop only calls the function 3 of the 6 times, so the issues appears to be the loop.
What in the world is going on?
Here is the button code:
private void btnProcess_Click(object sender, EventArgs e) {
foreach (DataGridViewRow row in gridData.Rows) {
// If Approved to Buy
if (Convert.ToBoolean(row.Cells["Approve"].EditedFormattedValue) == true) {
if (row.Cells["Action"].Value.ToString() == "Buy") {
ApprovedBuyAction(row.Index);
}
}
}
} // End btnPurchase Function
And here is the function being called:
private void ApprovedBuyAction(int rowNum) {
using (SqlConnection conn = new SqlConnection(Global.connString)) {
conn.Open();
string sqlInsertQuery = "INSERT INTO PORG_Process " +
"(SessionID, Date, Type, BuyerID, SourceLoc, VendorID, SupplierID, Item, UOM, Qty, processFlag, deleteFlag, computerName, userName) VALUES " +
"(@SessionID, @Date, @Type, @BuyerID, @LocationID, @VendorID, @SupplierID, @Item, @UOM, @Qty, @processFlag, @deleteFlag, @computerName, @userName)";
using (SqlCommand sqlInsert = new SqlCommand(sqlInsertQuery, conn)) {
sqlInsert.Parameters.Add("@SessionID", SqlDbType.VarChar, 60).Value = hdnSessionID.Text;
sqlInsert.Parameters.Add("@Date", SqlDbType.VarChar, 60).Value = DateTime.Now.ToString();
sqlInsert.Parameters.Add("@Type", SqlDbType.VarChar, 10).Value = "PO";
sqlInsert.Parameters.Add("@BuyerID", SqlDbType.VarChar, 60).Value = hdnBuyerID.Text;
sqlInsert.Parameters.Add("@LocationID", SqlDbType.VarChar, 60).Value = gridData.Rows[rowNum].Cells["Location"].Value;
sqlInsert.Parameters.Add("@VendorID", SqlDbType.VarChar, 60).Value = gridData.Rows[rowNum].Cells["Vendor"].Value;
sqlInsert.Parameters.Add("@SupplierID", SqlDbType.VarChar, 60).Value = gridData.Rows[rowNum].Cells["Supplier"].Value;
sqlInsert.Parameters.Add("@Item", SqlDbType.VarChar, 60).Value = gridData.Rows[rowNum].Cells["Item"].Value;
sqlInsert.Parameters.Add("@UOM", SqlDbType.VarChar, 10).Value = gridData.Rows[rowNum].Cells["UOM"].Value;
sqlInsert.Parameters.Add("@Qty", SqlDbType.VarChar, 60).Value = gridData.Rows[rowNum].Cells["FinalQty"].Value;
sqlInsert.Parameters.Add("@processFlag", SqlDbType.Int).Value = 1;
sqlInsert.Parameters.Add("@deleteFlag", SqlDbType.Int).Value = 0;
sqlInsert.Parameters.Add("@computerName", SqlDbType.VarChar, 60).Value = hdnMachineName.Text;
sqlInsert.Parameters.Add("@userName", SqlDbType.VarChar, 60).Value = hdnUserName.Text;
sqlInsert.CommandType = CommandType.Text;
sqlInsert.ExecuteNonQuery();
}
string sqlUpdateQuery = "UPDATE PorgReqs SET processFlag = 1 WHERE id = @TableID";
using (SqlCommand sqlUpdate = new SqlCommand(sqlUpdateQuery, conn)) {
sqlUpdate.Parameters.Add("@TableID", SqlDbType.Int).Value = gridData.Rows[rowNum].Cells["id"].Value;
sqlUpdate.CommandType = CommandType.Text;
sqlUpdate.ExecuteNonQuery();
}
}
// Remove the Row from the Grid
gridData.Rows.RemoveAt(rowNum);
} // End ApprovedBuyAction Function
Upvotes: 0
Views: 308
Reputation: 37066
This is the culprit, I believe, down at the end of ApprovedBuyAction()
:
gridData.Rows.RemoveAt(rowNum);
It looks like you're removing rows from the collection as you iterate over it: You delete item 0, then the former item 1 is now item zero. Next iteration, you move on to the new item 1, formerly item 2.
I'm surprised you got away with that without an exception.
I suggest that in your loop through dataGrid.Rows
, just make a list of the row indexes you want to delete. After that loop ends, loop through the list of indexes, passing each one to ApprovedBuyAction(idx);
in turn.
You could also iterate through a temporary copy of the collection:
foreach (var row in gridData.Rows.Cast<DataGridViewRow>().ToList())
{
...or in reverse, as Rufus suggests. I prefer to avoid for
loops when foreach
is an option, because when there's no index, I don't have to think about the index. But for
loops are hardly rocket science, even for me. Any of the three approaches will work.
Upvotes: 2
Reputation: 37070
The problem here appears that you're removing rows from the data grid as you iterate over them. In general, if you want to remove items from a collection while you iterate over it, you should start from the last index and move towards the first, to avoid sitations like this (when you remove an item, all the following items' indexes will change by -1
).
For example, you could replace your foreach
code with the following to walk backwards through the rows:
private void btnProcess_Click(object sender, EventArgs e)
{
for (int i = gridData.Rows.Count - 1; i > -1; i--)
{
DataGridViewRow row = gridData.Rows[i];
// rest of code remains unchanged
}
}
Upvotes: 2