none
Order of events when updating BindingSource. RRS feed

  • Question

  •  

    Hi all:

     

    C# .Net 2.0 Windows project.

     

    I am somewhat confused as to when certain events push changes from a DataGridView to the underlying BindingSource and from the BindingSource to the typed DataSet.

     

    I have code in the Cell_Leave event of the DataGridView that extracts the BindingSource and calls .EndEdit() on it. This is triggered by tabbing out of the cell.

     

    In theory the changes should then, at that point, be pushed to the DataSet. But if I check the .HasChanges() status of the DataSet right after .EndEdit(), no changes show.

     

    Once the event is handled the cursor is, naturally, in the next cell. Once I tab out of that cell, *even if no changes are made to that cell*, then the DataSet shows it has changes.

     

    So at what point is the BindingSource actually pushing changes to the DataSet? Must it wait for the DataGridView events to complete before doing its thing, even though I call .EndEdit() inside the Cell_Leave event?

     

    Thanks

     

    Kurt

     

    Namaste

    Friday, June 13, 2008 3:22 PM

Answers

  •  

    Hi Kurt,

    Validation is a required part of validating and updating the data in a bound control. Once the form and all bound controls are validated, any current edits need to be committed. Because of that the CellLeave trigger before the cell validation, there is no changes in the DataSet. The following identifies the order of validation, enter/leave and begin/end edit events. The EditMode is EditOnEnter.

    When moving from cell to cell (in the same row)

    1)      Cell Leave (old cell)

    2)      Cell Validating/ed (old cell)

    3)      Cell EndEdit (old cell)

    4)      Cell Enter (new cell)

    5)      Cell BeginEdit (new cell)

    When moving from one row to another you get:

    1)      Cell Leave (old cell), Row leave (old row)

    2)      Cell Validating/ed (old cell)

    3)      Cell EndEdit (old cell)

    4)      Row Validating/ed (old row)

    5)      Row Enter (new row)

    6)      Cell Enter (new cell)

    7)      Cell BeginEdit (new cell)

     

    To avoid this problem, you can try to use the CellValidated event to update the database.

    Code Snippet

            BindingSource bs = new BindingSource();

            DataSet ds = new DataSet();

     

            private void Form11_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable();

                dt.Columns.Add("id");

                dt.Columns.Add("name");

                dt.Columns.Add("activeStatus", typeof(bool));

                for (int i = 0; i < 50; i++)

                {

                    dt.Rows.Add(i, "name" + i, (i % 2) == 0);

                }

                dt.AcceptChanges();

                ds.Tables.Add(dt);

                bs.DataSource =ds;

                bs.DataMember = dt.TableName;

                this.dataGridView1.DataSource = bs;

                this.dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);

                this.dataGridView1.CellValidated += new DataGridViewCellEventHandler(dataGridView1_CellValidated);

            }

     

            void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e)

            {

                bs.EndEdit();

                MessageBox.Show(ds.HasChanges().ToString());

            }

           

            void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)

            {

                bs.EndEdit();

                MessageBox.Show(ds.HasChanges().ToString());

            }

     

    Please check the following article for details:
    http://www.windowsclient.net/Samples/Go%20To%20Market/DataGridView/DataGridView%20FAQ.doc

    Hope this helps.
    Best regards.
    Rong-Chun Zhang

    Windows Forms General FAQs
    Windows Forms Data Controls and Databinding FAQs

    Thursday, June 19, 2008 9:29 AM

All replies

  •  

    Ok, here’s another interesting/frustrating thing that is happening because of this “lag” or delay in the updates:

     

    If I add a new row to the DataGridView the DefaultsNeeded event fires and allows me to populate keys and/or other needed data in the grid. But the key field is hidden from the client’s view so the cursor actually winds up parked in the 1st cell of the new row. All fine, makes sense.

     

    Then you type in that cell and tab to the next cell. The Cell_Leave event fires and the update logic is called but because of the delay the actual data that is sent to the database is JUST the key data that was hidden. The addition/change you just typed into the 1st visible cell is NOT sent at this point.

     

    But if you, like me, refresh the underlying DataTable after each Update or Insert to ensure your client is seeing the most current data, then you are screwed because the text you just typed in the 1st visible cell is NOT in the database. The refresh from the database effectively wipes that text out of your grid.

     

    Wow. That stinks on so many levels. If this is confusing anyone I’ll go through it step by step.

     

    DataRow from DataTable looks like this:

    | fkJob | pkEvent | Name | EventStart | EventEnd | EventDesc |

     

    Actual displayed/visible row the client sees in the DataGridView looks like this:

    | Name | EventStart | EventEnd | EventDesc |

     

    1) User clicks into the first empty cell on the last row of the grid (Name), thus starting the logic to create a new row.

     

    2) This causes the Cell_Leave event to fire. Update logic is run but there’s nothing to update so we just bounce back to that empty cell.

     

    3) The Defaults_Needed event fires next. I manually stuff my needed fkJob value into the hidden fkJob cell of the grid. This event then calls Cell_Leave (I don’t know why) which in turn calls update logic. The update logic now succeeds so now the database row looks like this:

    | fkJob | pkEvent | Name | EventStart | EventEnd | EventDesc |

    |  14     |        6     | null    |     null        |      null      |    null         |

     

    4) Select logic runs, pulls the most current data into the DataSet and controls are rebound to display new data. The grid looks like this:

    | Name    | EventStart | EventEnd | EventDesc |

    |<empty>|  <empty>  | <empty>   | <empty>   |

     

    5) I type a name into the Name cell of the grid and tab out. Before *any* event fires the grid looks like this:

    | Name | EventStart | EventEnd | EventDesc |

    | Test    |  <empty>  | <empty>   | <empty>   |

     

    6) Tabbing away calls Cell_Leave. Update logic is run but NO changes show in the DataSet so the database has not changed at all. My cursor is in the next cell (EventStart) and the name Test still shows in the Name cell.

     

    7) I enter an event start date. I tab away. This fires Cell_Leave. Cell_Leave runs update logic which shows that there *are* changes ion the DataSet. These changes are pushed to the database and lo and behold – it’s the *last* change I made. Only the name Test is pushed to the database, which now looks like this:

    | fkJob | pkEvent | Name | EventStart | EventEnd | EventDesc |

    |  14     |        6     | Test    |     null        |      null      |    null         |

     

    But to keep all the client data fresh I pull from the database and repopulate my controls. Since my most recent change, the addition of an EventStart date, isn’t in the database my grid goes from this…

     

    | Name | EventStart | EventEnd | EventDesc |

    | Test    | 5/22/2008 | <empty>   | <empty>   |

     

    Back to this…

    | Name | EventStart | EventEnd | EventDesc |

    | Test    |  <empty>  | <empty>   | <empty>   |

     

    Disappearing data!

     

    8) Ignoring that I enter an EventEnd date then tab away. Update logic fires but since the EventStart date is now gone there are no changes to be pushed. Nothing happens with the database. I am dumped at the next cell and my grid looks like this:

    | Name | EventStart | EventEnd | EventDesc |

    | Test    |  <empty>  | 6/1/2008   | <empty>   |

     

    9) Now I enter and EventDesc and tab away. Cell_Leave fires, calling update logic. Changes are found but, again, they are the *last* changes. IOW, it writes the EventEnd date to the db so the db now looks like this:

    | fkJob | pkEvent | Name | EventStart | EventEnd | EventDesc |

    |  14     |        6     | Test    |     null        | 6/1/2008  |    null         |

     

    The select logic runs again to refresh controls and of course I lose the EventDesc I just entered. A lovely checkerboard pattern is starting to emerge.

     

    So, anyone know what can be done about this? WHY won’t my BindingSource push its changes to the DataSet in the Cell_Leave event as I instruct it to?

     

    Kurt

     

    Friday, June 13, 2008 5:40 PM
  •  

    Hi Kurt,

    Validation is a required part of validating and updating the data in a bound control. Once the form and all bound controls are validated, any current edits need to be committed. Because of that the CellLeave trigger before the cell validation, there is no changes in the DataSet. The following identifies the order of validation, enter/leave and begin/end edit events. The EditMode is EditOnEnter.

    When moving from cell to cell (in the same row)

    1)      Cell Leave (old cell)

    2)      Cell Validating/ed (old cell)

    3)      Cell EndEdit (old cell)

    4)      Cell Enter (new cell)

    5)      Cell BeginEdit (new cell)

    When moving from one row to another you get:

    1)      Cell Leave (old cell), Row leave (old row)

    2)      Cell Validating/ed (old cell)

    3)      Cell EndEdit (old cell)

    4)      Row Validating/ed (old row)

    5)      Row Enter (new row)

    6)      Cell Enter (new cell)

    7)      Cell BeginEdit (new cell)

     

    To avoid this problem, you can try to use the CellValidated event to update the database.

    Code Snippet

            BindingSource bs = new BindingSource();

            DataSet ds = new DataSet();

     

            private void Form11_Load(object sender, EventArgs e)

            {

                DataTable dt = new DataTable();

                dt.Columns.Add("id");

                dt.Columns.Add("name");

                dt.Columns.Add("activeStatus", typeof(bool));

                for (int i = 0; i < 50; i++)

                {

                    dt.Rows.Add(i, "name" + i, (i % 2) == 0);

                }

                dt.AcceptChanges();

                ds.Tables.Add(dt);

                bs.DataSource =ds;

                bs.DataMember = dt.TableName;

                this.dataGridView1.DataSource = bs;

                this.dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);

                this.dataGridView1.CellValidated += new DataGridViewCellEventHandler(dataGridView1_CellValidated);

            }

     

            void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e)

            {

                bs.EndEdit();

                MessageBox.Show(ds.HasChanges().ToString());

            }

           

            void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)

            {

                bs.EndEdit();

                MessageBox.Show(ds.HasChanges().ToString());

            }

     

    Please check the following article for details:
    http://www.windowsclient.net/Samples/Go%20To%20Market/DataGridView/DataGridView%20FAQ.doc

    Hope this helps.
    Best regards.
    Rong-Chun Zhang

    Windows Forms General FAQs
    Windows Forms Data Controls and Databinding FAQs

    Thursday, June 19, 2008 9:29 AM
  •  

    Thank you Rong!

     

    The Cell_Validating/ed events were definitely missing pieces in my mind. Your explanation made a lot of sense and I am especially grateful for the order of events you wrote out.

     

    Namaste

     

    Kurt

    Thursday, June 19, 2008 2:47 PM