如何将 DataGridViewComboBoxCell 转换为 DataGridViewTextBoxCell
How to convert DataGridViewComboBoxCell to DataGridViewTextBoxCell
我搜索了很多关于此错误的信息,许多相同的问题已经被问到,但它没有解决我的问题。
我得到
Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.
场景是我有 datagridview
和 TextboxColumn
我正在使用 CellBeginEdit
在 ComboBoxColumn
中转换它,在 CellValidate
之后我再次更改 ComboBoxColumn到文本框列。这些代码适用于所有人。但是在确切的行 e.RowIndex = 2
中出现上述错误会引发此异常,但其他行不会显示错误。如果我忽略此错误并继续,则 e.RowIndex = 2 单元格值变为空白,其他行值有效。
这里是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);
}
}
如何解决这个问题。
更新:
那里没有行,用户将添加新行和 select 值,最基本的是我想显示来自数据库的组合和填充值,填充值取决于条件,所以每次都会出现新值,
示例数据
testTable
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
在第 1 列中,我添加了一个值从 1 到 9 的组合,在 _mFillSieveSizeGridCombo
中,我将 id 传递给 sql server 2008 并使用 Combo.Item.Add(x)
方法填充组合。
SetCurrentCellAddressCore()
中有一个标志,防止任何重入调用破坏 DataGridView
的内部值。通常会引发一个事件,标志 = true 并在事件结束时重置。
要解决此问题,您只需在事件中添加 BeginInvoke()
的包装器,以便在事件发生后使用异步获取进程 运行。
编辑
问题可以在EditOnEnter
模式下重现,BeginInvoke
事件外的单元格setter导致无限循环
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;
}
}
}
正如您在执行此操作时遇到的麻烦所表明的那样,DataGridView 对您试图拉地板垫感到非常不满。它明确禁止在关键时刻更改单元格对象。而它在处理一个事件本身就是这样一个关键时刻。事件的一般问题,称为 重新进入 。你用@Eric 的方法遇到的麻烦表明这确实是一个棘手的问题。
那么您而不是要做的是修改单元格类型或引用。盯着球看,您真正想要做的是修改下拉列表的内容。那不是问题。返回设计器并将列的 ColumnType 属性 更改为 DataGridViewComboBoxColumn。并使用 CellBeginEdit 事件动态更改组合框项目集合。一个简单的例子:
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());
}
}
这里有一个解决方法:在 CellBeginEdit
事件中,首先检查 ColumnType
是否为 DataGridViewComboBoxCell
。如果不是我们 cancel
事件,调用一个更改列类型的函数,然后再次调用该事件:
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;
}
//..
现在您的代码可以继续,显然无需更改单元格。可能您还想传入要设置的文本值..
注意您必须确保 CellEndEdit
事件不会提前恢复更改!!也许一个标志,也许在 Tag
中会有帮助。如果你愿意,我可以看看你的 CellEndEdit
代码,如果有的话..
我搜索了很多关于此错误的信息,许多相同的问题已经被问到,但它没有解决我的问题。 我得到
Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.
场景是我有 datagridview
和 TextboxColumn
我正在使用 CellBeginEdit
在 ComboBoxColumn
中转换它,在 CellValidate
之后我再次更改 ComboBoxColumn到文本框列。这些代码适用于所有人。但是在确切的行 e.RowIndex = 2
中出现上述错误会引发此异常,但其他行不会显示错误。如果我忽略此错误并继续,则 e.RowIndex = 2 单元格值变为空白,其他行值有效。
这里是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);
}
}
如何解决这个问题。
更新: 那里没有行,用户将添加新行和 select 值,最基本的是我想显示来自数据库的组合和填充值,填充值取决于条件,所以每次都会出现新值,
示例数据
testTable
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
在第 1 列中,我添加了一个值从 1 到 9 的组合,在 _mFillSieveSizeGridCombo
中,我将 id 传递给 sql server 2008 并使用 Combo.Item.Add(x)
方法填充组合。
SetCurrentCellAddressCore()
中有一个标志,防止任何重入调用破坏 DataGridView
的内部值。通常会引发一个事件,标志 = true 并在事件结束时重置。
要解决此问题,您只需在事件中添加 BeginInvoke()
的包装器,以便在事件发生后使用异步获取进程 运行。
编辑
问题可以在EditOnEnter
模式下重现,BeginInvoke
事件外的单元格setter导致无限循环
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;
}
}
}
正如您在执行此操作时遇到的麻烦所表明的那样,DataGridView 对您试图拉地板垫感到非常不满。它明确禁止在关键时刻更改单元格对象。而它在处理一个事件本身就是这样一个关键时刻。事件的一般问题,称为 重新进入 。你用@Eric 的方法遇到的麻烦表明这确实是一个棘手的问题。
那么您而不是要做的是修改单元格类型或引用。盯着球看,您真正想要做的是修改下拉列表的内容。那不是问题。返回设计器并将列的 ColumnType 属性 更改为 DataGridViewComboBoxColumn。并使用 CellBeginEdit 事件动态更改组合框项目集合。一个简单的例子:
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());
}
}
这里有一个解决方法:在 CellBeginEdit
事件中,首先检查 ColumnType
是否为 DataGridViewComboBoxCell
。如果不是我们 cancel
事件,调用一个更改列类型的函数,然后再次调用该事件:
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;
}
//..
现在您的代码可以继续,显然无需更改单元格。可能您还想传入要设置的文本值..
注意您必须确保 CellEndEdit
事件不会提前恢复更改!!也许一个标志,也许在 Tag
中会有帮助。如果你愿意,我可以看看你的 CellEndEdit
代码,如果有的话..