0

I have a parent class defined as following:

using System.Collections.Generic;

namespace Test
{
    public class GeneralClass
    {
        public class Parent
        {
            public string Parent_Name { get; set; }
            public List<Child> List_Child { get; set; } = new List<Child>();
        }

        public class Child
        {
            public string Child_Name { get; set; }
        }
    }
}

Please note that the Child_Name has the following format: Parent_Name + "-" + an integer number.

Then in the same Form, I create two DataGridView (dt1 and dt2). On the dt1, each row shows the Parent_Name and on the dt2 each row shows the Child_Name. Each parent can have multiple children (List).

Now I want to: - Delete a parent (a row) on dt1, it would also delete all the associated children in dt2 (but not the children of other parent).

So far, what I've done is

// Iteration over selected parents
foreach (DataGridViewRow row_dt1 in dt1.SelectedRows)
{
    if (!row.IsNewRow)
    {
        // Find parent name of actual row
        string parent_name = row_dt1.Cells[0].Value.ToString();
        // Iteration over all rows of children
        foreach (DataGridViewRow row_dt2 in dt2.Rows)
        {
            // Find child name
            object val1 = row_dt2.Cells[0].Value;
            // If child name starts with parent name, remove this child from the DataGridView (dt2)
            if (val1 != null && val1.ToString().StartsWith(parent_name + "-"))
            {
                dt2.Rows.Remove(row_dt2);
            }
        }
        // Now remove the parent from dt1
        dt1.Rows.Remove(row_dt1);
    }
}

It deleted the selected parent as expected but it only deleted the first child of this parent (but not the others). Where did I do wrong?

Thank you very much!

TP Nguyen
  • 51
  • 6
  • 1
    It is easier and much more efficient to use data binding and iterate the datasource rather than fish around in controls for data – Ňɏssa Pøngjǣrdenlarp Apr 25 '19 at 15:31
  • If you're using the List(s) as the DGV's DataSource, remove/filter the items in the List(s). Then, set the DGV's `.DataSource = null;` and back to the filtered List (can be the result of a LINQ's `Where()`). Or use a `BindingSource` to link the List(s) or a BindingList instead of a simple List. – Jimi Apr 25 '19 at 15:37
  • I'm not using any DataSource here. – TP Nguyen Apr 25 '19 at 15:45

1 Answers1

0

You shouldn't try to remove items from the same collection you are iterating over.
If you remove an item from the collection the foreach iterator will be put in an impossible situation. It will be no more able to correctly find the next row in the iteration. It is like sawing the branch you are sitting on

The old trick to use here is to navigate the rows collection with a normal for..loop starting from the last item in the collection. So while you remove items, the counter (x) decrease and you don't skip any rows in your loop.

foreach (DataGridViewRow row_dt1 in dt1.SelectedRows)
{
    if (!row.IsNewRow)
    {
        // Find parent name of actual row
        string parent_name = row_dt1.Cells[0].Value.ToString();
        // Iteration over all rows of children
        for(int x = dt2.Rows.Count - 1; x >= 0; x--)
        {
            // Find child name
            DataGridViewRow row_dt2 = dt2.Rows[x];
            object val1 = row_dt2.Cells[0].Value;
            // If child name starts with parent name, remove this child from the DataGridView (dt2)
            if (val1 != null && val1.ToString().StartsWith(parent_name + "-"))
            {
                dt2.Rows.Remove(row_dt2);
            }
        }
        // Now remove the parent from dt1
        dt1.Rows.Remove(row_dt1);
    }
}
Steve
  • 213,761
  • 22
  • 232
  • 286