3

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; 
}
Steven
  • 13,501
  • 27
  • 102
  • 146
  • +1 This made me investigate further details about DataSets and DataBinding that I had never went through yet. Thanks for allowing me to learn as well. – Will Marcouiller Apr 30 '14 at 15:25

1 Answers1

3

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();
Community
  • 1
  • 1
Will Marcouiller
  • 23,773
  • 22
  • 96
  • 162
  • The Properties Editor only lets me set the BindingSource.DataSource to the DataSet. There is no button to expand further. If I change the value in the Designer.cs file, it reflects in the Properties window, but I still get the same error. – Steven Apr 30 '14 at 13:21
  • Doing it through the Form Designer may constraint some of the behaviour that is actually doable by code. I suggest you provide some further code sample that show how your `BindingSource`, `DataGridView`, `BindingNavigator` and the `DataSet` are initialized and later used. This might provide me with some more details in order to assist you further. – Will Marcouiller Apr 30 '14 at 13:39
  • I dragged the myTypes DataTable from the Data Sources window onto the form which added all of the following: `someDataSet`, `myTypesBindingSource`, `myTypesTableAdapter`, `tableAdapterManager`, `myTypesBindingNavigator`. I'll edit my original post to include more potentially relevant code. – Steven Apr 30 '14 at 13:48
  • How many tables do you have in `SomeDataSet`? A try to assign `BindingSource.DataSource = someDataSet.Tables[0]` might help the cause. Have you tried, once everything is created in design mode, to initialize values programmatically? – Will Marcouiller Apr 30 '14 at 14:51
  • Your code worked, but I wanted to assign values into a strongly-typed row. I figured it out (see final code appended in your answer). – Steven Apr 30 '14 at 15:13
  • Your final suggestion didn't work as `e.NewObject` is `null`. – Steven Apr 30 '14 at 15:40
  • I'll remove it then, to avoid misconception for the other community users. If you'd like, please send me an email with a example application, and I shall take an eye out and see if I can improve something: will.marcouiller(at)groupe-wmi.com – Will Marcouiller Apr 30 '14 at 16:49