Share via


DataTable.GetChanges(DataRowState.Modified) returns NULL

The scenario; you have a DataGridView on a Windows Form, you then manually, in code, create a DataTable and set this to be the DataSource of the DataGrid.

You then edit some of the data in the grid and then you need to get hold of a subset of data containing the Modified rows:

DataTable modifiedTable = dt.GetChanges(DataRowState.Modified);

int modifiedRowCount = modifiedTable.Rows.Count;

This however throws an exception

System.NullReferenceException was unhandled

  Message="Object reference not set to an instance of an object.";

This is a bit odd; you have clearly edited (Modified) the data, so why are no rows returned from the GetChanges(DataRowState.Modified) method?

In short, what you most likely are experiencing is that you have not called AcceptChanges() on the DataTable before you set it to be the DataSource of the DataGrid.

This will cause all rows (including the one you have edited) to be in the Added state. This will take precedence over the Modified state.

As usual, it is easier to show by example. Create a new Windows Form project; add a Button, a DataGridView and a ListBox to the Form.

Then replace the generated code with the code below.

    public partial class Form1 : Form

    {

        DataTable dt;

        public Form1()

        {

            InitializeComponent();

            dt = CreateTable();

            dataGridView1.DataSource = dt;

        }

        private void button1_Click(object sender, EventArgs e)

        {

            foreach (DataRow dr in dt.Rows)

            {

                listBox1.Items.Add(String.Format("Id: {0}, RowState: {1}", dr["id"], dr.RowState));

            }

            DataTable modifiedTable = dt.GetChanges(DataRowState.Modified);

            // Row below will fail with the NullReferenceException.

            //int modifiedRowCount = modifiedTable.Rows.Count;

        }

        private DataTable CreateTable()

        {

            DataTable dt = new DataTable();

            dt.Columns.Add(new DataColumn("id", typeof(int)));

            dt.Columns.Add(new DataColumn("FirstName", typeof(string)));

            dt.Columns.Add(new DataColumn("LastName", typeof(string)));

            dt.Rows.Add(new object[] { 1, "Mike", "Mikeson" });

            dt.Rows.Add(new object[] { 2, "John", "Johnson" });

            dt.Rows.Add(new object[] { 3, "Paul", "Paulson" });

            return dt;

        }

    }

Run it, edit some values in the columns and hit the button. This should produce the following output in the Listbox:

Id: 1, RowState: Added

Id: 2, RowState: Added

Id: 3, RowState: Added

And this will of course cause the dt.GetChanges(DataRowState.Modified) call to return NULL as specified in the documentation:

“If no rows of the desired DataRowState are found, the method returns null”

So, what you need to do here is simply to call AcceptChanges(), which will set all rows RowState to Unchanged, before setting it to be the DataSource of the grid.

By doing this, the changes you make will be detected and the rows will go into the Modified state. Simply add this before the DataTable is returned.

dt.AcceptChanges();

return dt;

Rerun the application make a change, this should now produce the following, allowing the dt.GetChanges(DataRowState.Modified) to return a datatable with one row.

Id: 1, RowState: Unchanged

Id: 2, RowState: Unchanged

Id: 3, RowState: Modified