"事件处理程序中的可重入调用SetCurrentCellAddressCore" - 仅在单元格行和列索引相等的情况下

Con*_*uit 4 c# datagridview exception event-handling winforms

我正在创建一个WinForms应用程序,其中包含一个使用a DataGridView来处理简单数据操作的表单.为了确保准确输入,同时减轻混乱(阅读:不使用DataGridViewComboBoxColumn)我有一些事件处理程序暂时将a DataGridViewTextBoxCell转换为等效DataGridViewComboBoxCell连接到已知为"干净"的值,当编辑事件被引发时(通常在单击可编辑单元格时):

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();
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

大多数情况下,这完全有效 - 编辑的最后一个单元格将恢复为DataGridViewTextBoxCell包含新选择的数据,并且选择进行编辑的单元格将DataGridViewComboBoxCell链接到mDropDownValues[]字典中指定的数据.但是,当编辑具有相等的行索引和列索引的单元格时,我遇到了麻烦.单元格在两种类型之间无法更改,dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell;在两个事件处理程序中都会抛出异常(一次处理时CellBeginEdit,然后再处理时CellEndEdit).例外情况

"操作无效,因为它导致对SetCurrentCellAddressCore函数的重入调用."

造成这个错误的原因是什么,为什么只有在沿着DataGridView对角线编辑单元格时才会发生?有没有办法在不抛出此异常的情况下实现此功能?

Han*_*ant 6

您正在使用SetCurrentCellAddressCore()中的此语句进行战斗,为了便于阅读而进行了编辑:

        // 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));
        }
Run Code Online (Sandbox Code Playgroud)

DATAGRIDVIEWOPER_inCurrentCellChange标志为true.DataGridView有很多这样的检查,无疑经过大量测试后添加.它限制的事情你可以做的数量,而引发事件,确保重入未在控制腐败的内部状态.

这种重入问题的通用解决方案是在事件提升并且控制的内部状态再次稳定之后执行操作.您可以使用BeginInvoke()方法优雅地执行此操作,它会在程序重新进入调度程序循环时运行.像这样:

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();
    }));
}
Run Code Online (Sandbox Code Playgroud)