“reentrant call to SetCurrentCellAddressCore” in event handlers - only where cell row and column indices are equal

孤者浪人 提交于 2021-02-07 08:01:37

问题


I am making a WinForms application which includes a form that uses a DataGridView to handle simple data manipulation. To ensure accurate entry while mitigating clutter (read: without using DataGridViewComboBoxColumn) I have a couple event handlers which temporarily turn a DataGridViewTextBoxCell into an equivalent DataGridViewComboBoxCell connected to values known to be "clean" when editing events are raised (typically when an editable cell is clicked):

private void OnCellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    //construct a textbox cell and set to current value
    DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();
    cell.Value = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;

    //color row if invalid or modified
    UpdateRowStyle(dataGridView.Rows[e.RowIndex]);

    //switch to the new cell and redraw
    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell;
    dataGridView.Refresh();
}

and

private void OnCellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
    //construct a combobox cell, link to data, and set to current value
    DataGridViewComboBoxCell cell = new DataGridViewComboBoxCell();
    cell.DataSource = mDropDownValues[dataGridView.Columns[e.ColumnIndex].Name];
    cell.Value = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;

    //switch to the new cell and redraw
    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell;
    dataGridView.Refresh();
}

Most of the time this works perfectly - the last cell edited reverts to a DataGridViewTextBoxCell containing the newly selected data, and the cell selected for editing becomes a DataGridViewComboBoxCell linked to the data specified in the mDropDownValues[] dictionary. When editing a cell which has row and column indices that are equal, however, I run into trouble. The cell fails to change between the two types, throwing an exception on the dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell; line in both event handlers (once when handling CellBeginEdit and then again when handlingCellEndEdit). The exception states

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

What is causing this error, and why does it only happen when editing the cells along the DataGridView's diagonal? Is there some way I can achieve this functionality without throwing this exception?


回答1:


You are doing battle with this statement in SetCurrentCellAddressCore(), edited for readability:

        // Allow the code to be re-entrant only as a result of
        // underlying data changing.
        if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] && 
           (this.dataConnection == null || !this.dataConnection.ProcessingListChangedEvent))
        {
            throw new InvalidOperationException(SR.GetString(SR.DataGridView_SetCurrentCellAddressCoreNotReentrant));
        }

The DATAGRIDVIEWOPER_inCurrentCellChange flag is true. DataGridView has a lot of these kind of checks, no doubt added after extensive testing. It limits the number of things you can do while an event is raised, ensuring that the re-entrancy does not corrupt internal state of the control.

The universal solution to these kind of re-entrancy problems is to perform the operation later, after the event is raised and the internal state of the control is stable again. You can do so elegantly with the BeginInvoke() method, it runs when your program re-enters the dispatcher loop. Like this:

private void OnCellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    this.BeginInvoke(new Action(() => {
        //construct a textbox cell and set to current value
        DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();
        cell.Value = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;

        //color row if invalid or modified
        UpdateRowStyle(dataGridView.Rows[e.RowIndex]);

        //switch to the new cell and redraw
        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell;
        dataGridView.Refresh();
    }));
}


来源:https://stackoverflow.com/questions/27626158/reentrant-call-to-setcurrentcelladdresscore-in-event-handlers-only-where-cel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!