4

I searched a lot for this error many same question is already asked, but its not solving my problem. I am getting

Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.

The scenario is I have datagridview with TextboxColumn I am using CellBeginEdit to convert it in ComboBoxColumn, and after CellValidate I again change ComboBoxColumn to TextboxColumn. The codes works for all. but getting said error in exact line e.RowIndex = 2 throws this exception, but others rows doses not show error. if I omit this error and continue then e.RowIndex = 2 cell value comes blank, and other rows value work.

Here is the code of CellBeginEdit

 if (e.ColumnIndex == 2 && e.RowIndex >= 0)
  {
    try
     {
      string s = Convert.ToString(_dgvCoarseAggegateTest[e.ColumnIndex, e.RowIndex].Value);
      string s1 = Convert.ToString(_dgvCoarseAggegateTest[e.ColumnIndex, 0].Value);
      DataGridViewComboBoxCell c = new DataGridViewComboBoxCell();

      string _SizeName = _cGetParrent._mGetParentCellValue(ref _dgvCoarseAggegateTest, e.RowIndex, 1);                  
      _mFillSieveSizeGridCombo(_mGetMetalSizeID(_SizeName), ref c); // Here My Combo Will GetValues from SQL and it Returning Value
      _dgvCoarseAggegateTest[e.ColumnIndex, e.RowIndex] = c; // Heres the error When e.RowIndex == 2 and if e.RowIndex != 2 then no error
      _dgvCoarseAggegateTest[e.ColumnIndex, e.RowIndex].Value = s;
      _dgvCoarseAggegateTest[e.ColumnIndex, 0].Value = s1;
     }
     catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

How to resolve this.

UPDATE : No rows there user will add new row and select values, the base thing is i want to show combo and fill values from database, the fill values is depends on condition, so every time new values will come,

Sample Data

testTable
1      A
2      B
3      C
4      D
5      E
6      F
7      G
8      H
9      I

In column1 I added one combo with values from 1 to 9, in _mFillSieveSizeGridCombo I am passing id to sql server 2008 and filling combo using Combo.Item.Add(x) method.

Imran Ali Khan
  • 8,469
  • 16
  • 52
  • 77
  • Strange. I can't reproduce. How many rows are there? Is the value in row 2 any different than others? Can you change it to one that works? does it happen with the mouse and with F2? (I'm only guessing, obviously ,-) Is anything special happening to the cell c in the mFillSieveSizeGridCombo due to its rownumber? – TaW Mar 15 '15 at 21:57
  • @taw no row there user will add new row and select values, the base thing is i want to show combo and fill values from database, the fill values is depends on condition, so every time new values will come, – Imran Ali Khan Mar 16 '15 at 07:31
  • If you comment the `_mFillSieveSizeGridCombo` call the cell's items don't get filled. but does the error still happen then? – TaW Mar 16 '15 at 07:36
  • Also: Are there any filters on the DGV or it's datasource/binding? – TaW Mar 16 '15 at 07:45
  • @taw DGV is not bind, it simple and i am adding rows by rows.add, and there is no filter – Imran Ali Khan Mar 16 '15 at 08:27
  • Can you please try to answer all my questions?! If you comment the `_mFillSieveSizeGridCombo` call, will the error go away? Also What happens when you call `mGetParentCellValue` with rowindex=1 always? – TaW Mar 16 '15 at 09:09
  • @taw if i omit _mFillSieveSizeGridCombo then same error comes, but combo not filling as _mFillSieveSizeGridCombo is filling combo values, and mGetParentCellValue is a just a getting any value you can take it abc anything upon this mGetParentCellValue value combo will filter – Imran Ali Khan Mar 16 '15 at 10:53
  • Hm, interesting! So the question remains, what is special about row 2? The Error often indicates that the row and/or column was about to change (but couldn't.). The Begininvoke has helped others, so that is worth trying harder. How is the layout; Is row in the middle of the rows, visible, unscrolled? Last idea: Do the Items fit with the cell value? – TaW Mar 16 '15 at 10:58
  • @taw Sorry c.Items.Add() is right – Imran Ali Khan Mar 16 '15 at 12:30
  • I have added a remark about possible issues in the CellEndEdit event to my answer, which otherwise works just fine.. – TaW Mar 16 '15 at 18:42

3 Answers3

2

There is a flag inside SetCurrentCellAddressCore() preventing any re-entrant call corrupting internal values of DataGridView. Usaully an event was raised with the flag = true and reset at the end of event.

To workaround this, you can simply add a wrapper of BeginInvoke() inside the event, to get your process run after the event with async.

EDIT

The issue can be reproduce in EditOnEnter mode, and the setter of cell outside the event in BeginInvoke results the indefinite loop

private bool _suppressCellBeginEdit = false;
private void dgv_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
    var dgv = sender as DataGridView;
    if (_suppressCellBeginEdit)
        return;

    if (e.ColumnIndex == 2 && e.RowIndex >= 0)
    {
        string s = Convert.ToString(dgv[e.ColumnIndex, e.RowIndex].Value);
        string s1 = Convert.ToString(dgv[e.ColumnIndex, 0].Value);
        DataGridViewComboBoxCell c = new DataGridViewComboBoxCell();

        c.Items.Add(string.Format("x{0}:y{1} {2}", e.RowIndex, e.ColumnIndex, 0));
        c.Items.Add(string.Format("x{0}:y{1} {2}", e.RowIndex, e.ColumnIndex, 1));
        c.Items.Add(string.Format("x{0}:y{1} {2}", e.RowIndex, e.ColumnIndex, 2));

        // special handling
        if (e.RowIndex == e.ColumnIndex)
        {
            this.BeginInvoke(new Action(() =>
            {
                _suppressCellBeginEdit = true;
                this.Invoke(new Action(() => 
                    {
                        c.Value = s;
                        dgv[e.ColumnIndex, e.RowIndex] = c;
                        dgv[e.ColumnIndex, 0].Value = s1;
                    }));
                _suppressCellBeginEdit = false;
            }));
        }
        else
        {
            c.Value = s;
            dgv[e.ColumnIndex, e.RowIndex] = c;
            dgv[e.ColumnIndex, 0].Value = s1;
        }
    }
}
Eric
  • 5,675
  • 16
  • 24
2

As you can tell from the trouble you are having implementing this, DataGridView is very unhappy about you trying to pull the floor mat. It explicitly forbids changing the cell object at critical moments. While it is handling an event itself is such a critical moment. A generic problem with events, called re-entrancy. The trouble you are having with @Eric's approach shows that it is indeed a tricky issue to solve.

So what you do not want to do is modify the cell type or reference. Keep your eyes on the ball, what you really want to do is modify the content of the dropdown list. That is not a problem. Get back to the designer and change the ColumnType property of the column to DataGridViewComboBoxColumn. And use the CellBeginEdit event to dynamically alter the combobox items collection. A simple example:

    private void _dgvCoarseAggegateTest_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) {
        var dgv = (DataGridView)sender;
        if (e.ColumnIndex == 2) {
            var cell = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
            cell.Items.Clear();
            // Run your dbase query here to fill cell.Items
            //...
            // We'll just fake it here for demo purposes:
            cell.Items.Add(e.RowIndex.ToString());
            cell.Items.Add((e.RowIndex+1).ToString());
            cell.Items.Add((e.RowIndex+2).ToString());
        }
    }
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • That was my first thought as well, but the original code works fine here and also at the OP, except for one cell in one row. Shouldn't it always come up with that error? – TaW Mar 16 '15 at 13:29
  • I didn't try the original code, the exception is however entirely normal for the *kind* of thing he's trying to do. It depends on exactly how the cell is put into edit mode, there's more than one way. – Hans Passant Mar 16 '15 at 13:49
  • I can do it with F2 or with the mouse without problems other than a funny feelig ;-) – TaW Mar 16 '15 at 13:50
  • 1
    Yes, the customization on dgv is too complicated - better using it in simple way like this one you suggest – Eric Mar 17 '15 at 02:04
  • >Unable to cast object of type 'System.Windows.Forms.DataGridViewTextBoxCell' to type 'System.Windows.Forms.DataGridViewComboBoxCell'. Error at var cell = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[e.ColumnIndex]; – Imran Ali Khan Mar 17 '15 at 07:59
  • 1
    Quote: "Get back to the designer and change the ColumnType property". You did not do that. – Hans Passant Mar 17 '15 at 08:43
1

Here is a workaround: In the CellBeginEdit event first check to see if the ColumnType is DataGridViewComboBoxCell. If it isn't we cancel the event, call a function that changes the column type and then call the event once more:

void switchCellType(object sender, DataGridViewCellCancelEventArgs e)
{
    DataGridViewComboBoxCell c = new DataGridViewComboBoxCell();
    // prepare the cell:
    //..
    // fill the drop down items..
    c.Items.Add("1");  // use
    c.Items.Add("2");  // your 
    c.Items.Add("3");  // code here!
    DGV[e.ColumnIndex, e.RowIndex] = c;  // change the cell
    DGV_CellBeginEdit(sender, e);        // restart the edit with the original parms  
}


private void DGV_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{

    DataGridViewCell cell = DGV[e.ColumnIndex, e.RowIndex];
    if (!(cell is DataGridViewComboBoxCell))
    {
        e.Cancel = true;
        switchCellType(sender, e);
        return;
    }
    //..

Now your code can proceed, obviously without the cell changing. Possibly you want to pass in the text value to set as well..

Note that you will have to make sure that the CellEndEdit event doesn't revert the change before its time!! Maybe a flag, maybe in the Tag will help. If you want I can have a look at your CellEndEdit code, if there is any..

TaW
  • 53,122
  • 8
  • 69
  • 111