Ctrl+Shift+B

Compilations by Steve Majewski
posts - 207, comments - 82, trackbacks - 2

My Links

News


The ideas contained herein are mine and mine alone, and do not reflect the beliefs of my employer, family, friends, faith, or society in general. Reader discretion advised.


Memberships

Avanade
Solutions Developer

Xbox Live Gamer Card

Article Categories

Archives

Post Categories

Resources

Blogroll

Podcasts

Miscellaneous

DataGridView Reentrant Call Nightmare

The story begins with the QA team finding a bug. The description of the bug appears quite simple. When the user clicked on a cell in a DataGridView on the form then closed the form using the X, reopening the form would throw an unhandled exception. "No problem," I think to myself. Little did I know the adventure on which I was about to embark.

Naturally, my first stop at debugging was to get the actual error that occurred. I place it here in the hopes that the wary developer scouring the web for a solution to a particularly nasty bug (get on with it) will find it and be saved:

System.InvalidOperationException was unhandled
Message="Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function."

The error message appeared simple enough. It even contained pretty words that make it much easier when finding solutions on Google (ahem...I mean MSN Search). Sadly, my search yielded me annoying solutions with no results.

Let me start (you're starting now?) by explaining what is happening. When the X button is hit, the form's focus does not change from the DataGridView. As a result, the grid remains in an unedited state. This particular form was being reused as a dialog, so the next time the form opened and code tried to change the grid's data, the error would occur.

The error is caused by an infinite loop. The cell on the grid is trying to change, thus calling the SetCurrentCellAddressCore, which in turn raises some other event that calls the SetCurrentCellAddressCore and so on and so forth. .NET detects the loops and gracefully crashes headlong into the ground like an ogre on ice skates.

The craziest part was the error would only occur when the application was run directly. When I ran it through VS in debug mode, it would continue on its merry way like nothing happened. Wrapping the offending call in a TRY...CATCH block also did not work. The interactive debugger would catch the exception but running the application direct (off the same compile, mind you) would throw the error every time despite the TRY...CATCH.

One solution I found involved deriving my own DataGridView class and overriding the SetCurrentCellAddressCore method. A property was added the would indicate whether or not the form was reloading data and prevent the reentrant call. While this worked for me when setting the grid's DataSource to null, it caused an IndexOutOfRangeException when I tried setting the DataSource later.

During my debugging, I could see good and well that the grid thought it was being edited thanks to the IsCurrentCellInEditMode property. I tried calling the CancelEdit, EndEdit, and even NotifyCurrentCellDirty methods, but the grid remained in edit mode. I tried directly setting the CurrentCell to null. No dice. The grid refused to realize that it really wasn't being edited.

Finally, light dawned on marble head. The problem is the grid is still active when the form is closed. What if I just selected another control when closing the form? There was no reason not to set the focus back to the first control on the form so that the next time the form opened it would be selected.

Even though I had code in place to switch the control focus, it was triggered when the form was opened (activated really) rather than when it was closed. The only thing I can deduce is the grid goes into a strange limbo if the form is closed whilst it is in edit mode. I created a FormClosing event and added the one line of code to select the first control on the form. That was enough to switch the grid out of edit mode so that it could be manipulated the next time the form was opened.

So what started as an easy looking bug ended with an easy solution with about 10 hours of hair pulling antics piled up in the middle. Either way, I still got the warm fuzzies when I saw it work. That is the mark of a true developer.

Technorati tags: , ,

Print | posted on Thursday, May 17, 2007 9:01 AM

Feedback

Gravatar

 re: DataGridView Reentrant Call Nightmare

this situation is my problem , but does not understanding. will give me to sample
10/26/2007 12:04 PM | glenda

 re: DataGridView Reentrant Call Nightmare

A sample isn't going to do much good. Add a FormClosing event to the form containing the DataGrid and in the event set the focus to another control. It doesn't matter which control as long as it is NOT the DataGrid.
11/7/2007 9:50 AM | Steve

 re: DataGridView Reentrant Call Nightmare

thank you, nice written :) and you really helped to save my hours... thanks!
11/10/2008 9:39 AM | david
Gravatar

 re: DataGridView Reentrant Call Nightmare

You can also disable and re-enable the dataGridView control to force it to commit the edit before the form gets closed.
1/29/2009 3:42 PM | Bill

 re: DataGridView Reentrant Call Nightmare

Thanks for posting this. Personally, I think the mark of a true developer is to question the design of the DataGridView control, put the kludge you discovered in place, then feel dirty for several days because you know, KNOW damn well that such a kludge would be unnecessary with a properly designed control.

Looks like we found out what the designers of MFC ended up coding...
5/6/2009 10:39 PM | Dave

 re: DataGridView Reentrant Call Nightmare

I had the same problem. The focus story did not help. At the end, I saw, that I forgot one of the binding sources to set to EndEdit.

Me.Validate()
TObjektBindingSource.EndEdit()
TAntragBindingSource.EndEdit()
TAttachmentsBindingSource.EndEdit()
....
solved the problem.
5/12/2009 3:35 AM | Peter
Gravatar

 re: DataGridView Reentrant Call Nightmare

Woe is me, for I have the same exception thrown, but not when he form is closed... :'-(
Inside a cellEndEdit method I try to clear the DataGridView, and that clearing raises the exception. Trying to focus on another control or trying to disable the DataGridView doesn't work...

I'm really starting to hate DataGridView....
5/21/2009 12:05 PM | Samuel

 re: DataGridView Reentrant Call Nightmare

Thank you so much for posting this you saved me hours. awesome.
6/11/2009 11:31 AM | Fabian

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 5 and 4 and type the answer here:

Powered by: