Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > InvalidOperationException with DataGridView bound to object data source
 

InvalidOperationException with DataGridView bound to object data source

I am getting an InvalidOperationException with the message "Operation is not valid due to the current state of the object." when using the DataGridView and an object data source in 2.0. After going crazy trying to find the source in a larger application I created a very simple app to see if I could reproduce the error and found that even a simple example application causes it.

Here are the steps that reproduce it:

1) Create a new project with a single form with only a DataGridView in it.
2) Create a simple object with two properties. For example:
public class Widget
{
private string _name;
private int _quantity;
public Widget()
{
_name = "";
_quantity = 0;
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public int Quantity
{
get { return _quantity; }
set { _quantity = value; }
}
}
3) Compile the project so the data source wizard can find the Widget class
4) Create an object data source for the Widget class (Data Menu > Add New Data Source > Select "Object" > Navigate to the Widget class)
5) Select this data source for the grid (DataGridView properties > DataSource > Other Data Sources > Project Data Sources > Widget).

Start up the project and everything looks fine. You can add, edit, and delete Widget rows.

Now try this:
6) Delete all rows out of the grid
7) In the first cell (Name column) type something to start a new row
8) Tab over to the Quantity column
9) Push esc to cancel editing the row
10) InvalidOperationException is thrown with the message "Operation is not valid due to the current state of the object."

This is the only scenario that causes this to happen. In any other row this would simply cause the row to be removed since it is new. The exception is only thrown when canceling the edit of a new first row, only after you edit one of the cells, and only if you are not currently editing another cell.

When I originally ran into this problem I was binding the grid to a class inherited from BindingList<Widget> and the same exception was being thrown. Since I had overridden some of the methods in the BindingList class I found that after pushing escape the following happens in the BindingList class:
1) RemoveItem is called. <- Ok, we are canceling a new item so the current item needs to be removed from the collection.
2) AddNewCore is called <- This makes sense, since the user is on the first row the previous row can't be selected so we need to start a new one.
3) AddNewCore is called AGAIN <- This is where the exception occurs and I don't understand why it's adding two new core objects to the collection. The exception is thrown from the DataGridView.DataGridViewDataConnection.ProcessListChanged method which is apprently checking for unexpected events from the data source.

Is there something I am missing or is this a bug in the framework? If it's the latter how do I work around it?

cgsteve  Thursday, March 16, 2006 6:29 PM

Use the following custom grid:

using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;

namespace TTVisual
{
[System.ComponentModel.DesignerCategory("Code")]
public class TTGridControl : DataGridView
{
public TTGridControl()
{
}

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Escape)
{
if (Rows.Count==2 && CurrentCell.RowIndex==0)
{
IBindingList bl = (IBindingList)DataSource;
bl.Clear();
return true;
}
}

return base.ProcessCmdKey(ref msg, keyData);
}
}
}

Mustafa Kamil ATA  Wednesday, March 21, 2007 6:20 PM

Hi There,

Have you found a solution to this problem yet? I am having exactly the same issue (but with VB.NET) and can't seem to find a way around it.

Cheers,

Phil.

Phil Wherrett  Monday, March 27, 2006 4:07 AM
Nope, I've posted it in a couple of places and no one has found a solution yet.
cgsteve  Monday, March 27, 2006 2:16 PM
I had the same problem in a simple experiment i did.
The reason is the overloading of OnAddNewCore or AddNewCore

My implementation that works is:

protected override void OnAddingNew( AddingNewEventArgs e )
{
base.OnAddingNew(e);
if (e.NewObject != null)
{
T t = e.NewObject as T;
//do my stuff here
}
}

It seems that OnAddingNew is called sometimes with e.NewObject NULL, and IT IS A BAD MOVE TO CREATE AN OBJECT at THAT TIME.

My implementation that did not work:

protected override void OnAddingNew( AddingNewEventArgs e )
{
e.NewObject = new T();
}
Liviu Uba  Tuesday, April 04, 2006 11:34 PM
Sorry

I wasn't clear about the stuff myself.
I think the reason is that your object and my object doesn't implement IeditableObject, and is not able to remove itself from the collection.

The problem is that the Grid or the BindingList calls CancelEdit before EndEdit and your object must detect this and remove itself from the list.

Another Gotcha is that on the BindingList<T> the IList.Remove( object o ) is not working.
You have to call BindingList<T>.removeAt(int index) in order to get the things done


Liviu Uba  Wednesday, April 05, 2006 12:22 PM

Use the following custom grid:

using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;

namespace TTVisual
{
[System.ComponentModel.DesignerCategory("Code")]
public class TTGridControl : DataGridView
{
public TTGridControl()
{
}

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Escape)
{
if (Rows.Count==2 && CurrentCell.RowIndex==0)
{
IBindingList bl = (IBindingList)DataSource;
bl.Clear();
return true;
}
}

return base.ProcessCmdKey(ref msg, keyData);
}
}
}

Mustafa Kamil ATA  Wednesday, March 21, 2007 6:20 PM

I was receiving this error (Operation is not valid due to the current state of the object) for two reasons. One was that I had not implemented the ICancelNew interface so this.Add(x) in the AddNewCore method failed when AddNew had not been ended or canceled. The following ICancelNew interface in my collection class (IBindingList<mytype> derived) solved that problem:

#region ICancelAddNew Members

void ICancelAddNew.CancelNew(int itemIndex)

{

CancelNew(itemIndex);

}

void ICancelAddNew.EndNew(int itemIndex)

{

EndNew(itemIndex);

}

#endregion

The second problem was what appears to be a bug in the DataGridView. It was solved by creating and usingthe new component described in the previous post (by Mustafa Kamil ATA).

AMMPCP  Monday, June 18, 2007 9:32 PM

Hi there,

though I've got the same problem and it seems to be nearlyone year ago since this questions has been aswered, I've searched and found an article

I'll try it now and leave messages of mysuccess here.

Have a nice weekend

Christian

Chrissivo  Friday, September 07, 2007 12:29 PM
You are right! Under varrious conditions the DataGridView seems to pass Null records. I noticed that MS has patched a few similar bugs, but they are only available by contacting them directly. In my case, this solution didn't quite work... but it did get me on the right track. Here is my solution:

public class SafeBindingList<T> : System.ComponentModel.BindingList<T>
{
/// <summary>
/// determines if OnAddNew was passed a bogus value
/// </summary>
bool failedToAdd = false;


// ========================================================================
/// <summary>
/// work-around for the case in which OnAddingNew was passed a bogus
/// NewObject record
/// </summary>
// ========================================================================
protected override void
OnListChanged(System.ComponentModel.ListChangedEventArgs e)
{
if (!failedToAdd)
{
base.OnListChanged(e);
}
failedToAdd = false;
}


// ========================================================================
/// <summary>
/// work-around to detect arugments with bogus NewObject record
/// </summary>
// ========================================================================
protected override void
OnAddingNew(System.ComponentModel.AddingNewEventArgs e)
{
failedToAdd = e.NewObject == null;
base.OnAddingNew(e);
}
}

If you are using a container other than BindingList you will have to implement similar methods.

--Josh
frustrated_cs_programmer  Wednesday, March 05, 2008 4:32 AM
I have a problem with this because after impletmenting this solution. I am not longer getting hiting the ListChanged Event when a new item is added. I had some logic to set default values when ListChangedType is ItemAdded. I am going to try to figure out a different way to set these. Any suggestions would help. I am trying to set a database flag to 'A' if it is a new item in the binding list.

Also, this not only happens with the esc key, but it happens when you delete the first row (only row) of a grid, leave focus of that grid and then go back to the grid.
Milton Waddams  Tuesday, April 08, 2008 8:19 PM

You can use google to search for other answers

Custom Search

More Threads

• ToolTip Garbage Collection Bug
• autocomplete AND dropdownlist combobox
• DataGridView Cell value (DBNull instead of NULL)
• "Freeze" datagridview sorting and filtering
• Inconsistent performance calling Oracle procedure
• What is the difference between CellDoubleClick and CellMouseDoubleClick?
• Pressing Escape on DataGridView Crashes!
• DataGrid data loading blocks the application
• how to insert an image into Ms access database using c#
• How to control order of column in linq to sql?