I want to customize the addition of a new item into a BindingSource
(all strongly-typed) as described on the following MSDN Article:
How to: Customize Item Addition with the Windows Forms BindingSource
The code below results in InvalidOperationException
: Objects added to a BindingSource's list must all be of the same type. Also, the object myTypesBindingSource.Current
seems to be a DataRowView
with my relevant row inside.
How can I customize the addition of a strongly-typed BindingSource
?
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.someDataSet = new myDB.SomeDataSet();
this.myTypesBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.myTypesTableAdapter = new myDB.SomeDataSetTableAdapters.myTypesTableAdapter();
this.tableAdapterManager = new myDB.SomeDataSetTableAdapters.TableAdapterManager();
this.myTypesBindingNavigator = new System.Windows.Forms.BindingNavigator(this.components);
this.someIntValueTextBox = new System.Windows.Forms.TextBox();
// someDataSet
this.someDataSet.DataSetName = "SomeDataSet";
this.someDataSet.SchemaSerializationMode = System.Data.SchemaSerializationMode.IncludeSchema;
// myTypesBindingSource
// As generated:
// this.myTypesBindingSource.DataMember = "myTypes";
// this.myTypesBindingSource.DataSource = this.someDataSet;
this.myTypesBindingSource.DataSource = this.someDataSet;
this.myTypesBindingSource.AddingNew += new System.ComponentModel.AddingNewEventHandler(this.myTypesBindingSource_AddingNew);
// myTypesTableAdapter
this.myTypesTableAdapter.ClearBeforeFill = true;
// tableAdapterManager
this.tableAdapterManager.BackupDataSetBeforeUpdate = false;
this.tableAdapterManager.myTypesTableAdapter = this.myTypesTableAdapter;
this.tableAdapterManager.UpdateOrder = myDB.SomeDataSetTableAdapters.TableAdapterManager.UpdateOrderOption.InsertUpdateDelete;
// someIntValueTextBox
this.someIntValueTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.myTypesBindingSource, "someIntValue", true));
this.someIntValueTextBox.Name = "someIntValueTextBox";
}
private void myTypesBindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
SomeDataSet.myTypesRow newRow = someDataSet.myTypes.NewmyTypesRow();
newRow.someIntValue = 99;
e.NewObject = newRow;
}
In the example, it is not a strongly typed BindingSource
. As a matter of fact, the AddingNewEventArgs.NewObject
property is an object. So, assigning it any derived type shall make it.
Also, notice that the example uses a class object DemoCustomer
, it is no DataSet.Tables[0].Row
which returns a DataRow
. The game is a bit different, in my point of view, when using a DataSet
.
When one uses a DataSet
, you'll have to set only a DataTable
as the BindingSource.DataSource
, making you write something like:
BindingSource.DataSource = DataSet.Tables[0];
This way, when you add an item to the BindingSource.List
using BindingSource.AddNew()
, the BindingSource
"knows" that it has a DataTable
as its DataSource
, so it calls the DataTable.NewRow()
method and a new DataRow
is added to your DataTable
! Thus, having a DataRow
to handle instead of a simple object.
Working with a DataRow
If you want to do similar to what the example on MSDN says, you'll have to create the row yourself.
DataRow newRow = DataSet.Tables[0].NewRow();
newRow.Columns["intColumn"] = 99;
e.NewObject = newRow;
This way, you shall be able to tell what default values you want.
Otherwise, if not, you might as well try this:
var newRow = (DataRow)e.NewObject;
newRow["intColumn"] = 99;
The weakness here is whenever you change the underlying database table column name, you'll have to come here, change the name of your intColumn
, and recompile, and redeploy.
Besides, this shall not happen often, so it might be worthy depending on your environmental context.
EDIT #1
After having paid more attention to:
Also, the object myTypesBindingSource.Current seems to be a DataRowView with my relevant row inside
From MSDN: DataRowView
Whenever data is displayed, such as in a DataGrid control, only one version of each row can be displayed. The displayed row is a DataRowView.
A DataRowView can have one of four different version states: Default, Original, Current, and Proposed.
After invoking BeginEdit on a DataRow, any edited value becomes the Proposed value. Until either CancelEdit or EndEdit is invoked, the row has an Original and a Proposed version. If CancelEdit is invoked, the proposed version is discarded, and the value reverts to Original. If EndEdit is invoked, the DataRowView no longer has a Proposed version; instead, the proposed value becomes the current value. Default values are available only on rows that have columns with default values defined.
This means that when adding a new row, you're actually adding a DataRowView
. You may access the current row by accessing its DataRowView.Row
property.
Taking this into consideration, you might perhaps change the proposed solution in my initial answer to this:
var newRow = ((DataRowView)e.NewObject).Row;
newRow.Columns["intColumn"] = 99;
EDIT: (by Steven) Final Code
DataView dv = (DataView)myTypesBindingSource.List;
DataRowView drv = dv.AddNew();
SomeDataSet.myTypesRow newMyTypesRow = (SomeDataSet.myTypesRow)drv.Row;
newMyTypesRow.someIntValue = 53;
e.NewObject = drv;
myTypesBindingSource.MoveLast();