我正在制作一个WinForms应用程序,其中包括一个使用DataGridView
来处理简单数据操作的表单。为了确保在减少混乱的同时准确输入(阅读:不使用DataGridViewComboBoxColumn
),我有几个事件处理程序,它们暂时将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();
}
和
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();
}
大多数情况下,这很完美 - 最后编辑的单元格恢复为包含新选择的数据的DataGridViewTextBoxCell
,并且选择进行编辑的单元格将成为链接到mDropDownValues[]
字典中指定的数据的DataGridViewComboBoxCell
。但是,在编辑行索引和列索引相等的单元格时,我遇到了麻烦。单元格无法在两种类型之间更改,在两个事件处理程序的dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex] = cell;
行上引发异常(处理CellBeginEdit
时一次,然后在处理CellEndEdit
时再次)。异常状态
"操作无效,因为它会导致对 SetCurrentCellAddressCore 函数的重入调用。
是什么导致了这个错误,为什么它只发生在编辑DataGridView
对角线的单元格时?有没有办法在不引发此异常的情况下实现此功能?
您正在与 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));
}
DATAGRIDVIEWOPER_inCurrentCellChange标志为真。 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();
}));
}