Initializing DataRows
How many ways are there to initialize a new DataRow with new values?
-
Set the DefaultValue property on the respective DataColumns.
This works fine for static values. However, you’re likely using the DataSet designer in Visual Studio, and it will hose itself if you try to set a GUID because it can’t tell them apart from a string. Plus, initializing with dynamic values like
DateTime.Nowwon’t work because you’ll end up with the date of the creation of the column rather than the date of the row. -
Initialize after creating it, but before adding it to the DataTable.
If you’re creating the row manually, you can just set any initial values before adding it to the table. None of the values, indices, etc. are validated until that happens. Strong typed datasets are even nice enough to generate methods that take and return rows of your type.
MyDataSet ds = new MyDataSet(); MyTableRow row = ds.MyTable.NewMyTableRow(); row.ID = new Guid(); row.CreatedOn = DateTime.Now; ds.MyTable.AddMyTableRow(row);
-
Override the OnTableNewRow of the DataTable in a derived class. This doesn’t actually work.
Like the derived classes created by the strong typed table generator. You can get to this method if you use the DataSet’s helpful partial class definition:
// Note: does not actually work public partial class MyDataSet { public partial class MyTable { protected override void OnTableNewRow(DataTableNewRowEventArgs e) { MyDataSet.MyTableRow row = (MyDataSet.MyTableRow)e.Row; row.ID = new Guid(); row.CreatedOn = DateTime.Now; base.OnTableNewRow(e); } } }
It doesn’t work because this method is never called unless you also attach a handler to the TableNewRow event, defeating the whole point of creating a virtual method. GG, Microsoft. This set me back a whole afternoon trying to figure out why some of my tables ran the method and others didn’t.
You can overcome this if you…
-
Handle the TableNewRow event of the DataTable.
Good if you want to initialize with external values of some kind, but you can also attach the handler from inside the typed DataTable if you override the
EndInit()method, which being part ofISupportInitialize, Visual Studio calls every time the table is initialized in a designer. If you use the dataset outside of the designer, well, you’re screwed.public partial class MyDataSet { public partial class MyTable { protected override void EndInit() { this.TableNewRow += (object sender, TableNewRowEventArgs e) => { // use 'delegate' if not C# 3.0 MyDataSet.MyTableRow row = (MyDataSet.MyTableRow)e.Row; row.ID = new Guid(); row.CreatedOn = DateTime.Now; }; base.OnTableNewRow(e); } } }
-
Use a BindingSource and handle the AddingNew event.
You bound your dataset to a bunch of controls, and now you want to create new rows with data that’s only available in your presentation tier. BindingSources let you control the row creation process completely if you handle the AddingNew event. The downside is that you have to create the whole thing manually, and to do that you have to know how BindingSources work.
Another caveat is that the BindingSource’s Position won’t move to the newly created item, like it would otherwise. You’ll have to do that yourself.
myBindingSource.AddingNew += (object sender, AddingNewEventArgs e) => { DataRowView view = ((DataView)myBindingSource.List).AddNew(); MyDataSet.MyTableRow row = (MyDataSet.MyTableRow)view.Row; row.ID = new Guid(); row.CreatedOn = DateTime.Now; e.NewObject = view; }
There’s a better way in which you don’t have to create the DataRowView…
-
Use a BindingSource and handle the ListChanged event.
This is the easiest way to initialize rows directly on the BindingSource. Unlike the AddingNew method, the BindingSource creates the row, and sets the Position to the new item.
myBindingSource.ListChanged += (object sender, ListChangedEventArgs e) => { if (e.ListChangedType == ListChangedType.ItemAdded) { DataRowView view = (DataRowView)myBindingSource[e.NewIndex]; if (view.IsNew) { // Check if the row is in its detached state MyDataSet.MyTableRow row = (MyDataSet.MyTableRow)view.Row row.ID = new Guid(); row.CreatedOn = DateTime.Now; bs.ResetItem(e.NewIndex); // this is needed to get the controls to show the new values that we just set. } } }
The ListChanged event is called twice. First when the row is created in its detached state, and then again when it’s added to the Rows collection of the DataTable. We only need to handle it the first time. The call to ResetItem will make sure that all the bound controls read the new values.
-
Use control-specific events.
Some controls have their own events to initialize new rows. I don’t recommend using this approach, because it’s too control-specific. It’s much more interoperable to initialize at the BindingSource. You never know how that AddNew might be called.
Depending on what kind of data you’re initializing, and in which tier, you might need to mix and match. #4 for the data tier and #6 for the forms do the job for me.
2 Responses to “Initializing DataRows”
Comment from Quazer
Time January 27, 2010 at 8:49 am
But in case №3
Wrong: base.OnTableNewRow(e);
Right: base.EndInit();
Comment from Quazer
Time January 26, 2010 at 3:46 pm
Thank you very much!