操作无效,原因是它导致对 SetCurrentCellAddressCore 函数的可重入调用。

本文解决了一个关于DataGridView单元格编辑完成后实时保存数据的问题。通过使用CellEndEdit事件,并结合异步调用的方式,避免了鼠标离开单元格时出现的运行时错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

即时保存DataGridView单元格修改,添加了CellEndEdit事件,在事件中包含重绘页面和DataGridView,当用鼠标离开单元格时报RT错,用键盘离开时一切正常。

“百度”推荐方法1:调用事件修改为CellValueChanged。
结果:触发的地方太多,程序大规模混乱。。。。。

“百度”推荐方法2:异步调用

private void dgv_CellEndEdit(object sender, DataGridViewCellEventArgs e)


{


   if (DialogResult.OK == MessageBox.Show("是否要修改", "", MessageBoxButtons.OKCancel))


  {


Users user = new Users();


     user.UserName = this.dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString();


     user.Password = this.dataGridView1.Rows[e.RowIndex].Cells[1].Value.ToString();


     string sql = "update [用户表$] set 用户名='" + user.UserName + "',密码='" + user.Password + "' where 用户名='" + user.UserName + "'";


     int count = OledbHelper.ExecuteNonquery(sql);


    if (count == 1)


    {


MessageBox.Show("修改成功");


      Thread thread = new Thread(DoWork);


      thread.Start();


 }


}


  }


public delegate void MyInvoke();


public void DoWork()


 {


 MyInvoke mi = new MyInvoke(LoadData);


 this.BeginInvoke(mi);


 }
结果:嘿嘿,解决了









<think>我们正在处理一个关于DataGridView设置CurrentCell时引发SetCurrentCellAddressCore可重入调用的问题。根据引用[2]的内容,我们知道DataGridView的操作模型在限制用户焦点到指定单元格方面需要重写一些方法,其中包括SetCurrentCellAddressCore。问题描述:当使用dgv.CurrentCell=cell设置当前单元格时,可能会引发SetCurrentCellAddressCore可重入调用(即在这个方法执行过程中又被调用),这通常会导致异常或程序不稳定。分析原因:在DataGridView中,设置CurrentCell属性会触发SetCurrentCellAddressCore方法的调用。如果在SetCurrentCellAddressCore方法执行过程中,又有代码尝试设置CurrentCell(例如在事件处理程序中再次设置),就会形成可重入调用(重入),这类似于递归调用但并非有意的,可能导致堆栈溢出或状态不一致。解决方案:为了避免可重入调用,我们需要在设置CurrentCell之前检查当前是否已经处于设置过程中。我们可以通过设置一个标志变量来指示当前是否正在设置单元格,从而避免重入。步骤:1.在自定义的DataGridView子类中,添加一个布尔类型的成员变量(例如_isSettingCurrentCell),用于标记是否正在设置当前单元格。2.重写SetCurrentCellAddressCore方法,在这个方法中设置标志变量,并在方法结束时重置。3.在设置CurrentCell之前,先检查这个标志变量。如果正在设置,则跳过本次设置。示例代码:我们创建一个自定义的DataGridView控件,比如叫做CustomDataGridView,并重写SetCurrentCellAddressCore方法。注意:在重写SetCurrentCellAddressCore时,要确保在基类方法执行前后设置和重置标志。同时,在设置当前单元格时(例如在事件处理中),先检查标志以避免重入。代码示例:```csharppublicclassCustomDataGridView:DataGridView{privatebool_isSettingCurrentCell=false;protectedoverridevoidSetCurrentCellAddressCore(intcolumnIndex,introwIndex,boolsetAnchorCellAddress,boolvalidateCurrentCell,boolthroughMouseClick){//如果已经在设置中,则直接返回,避免重入if(_isSettingCurrentCell)return;try{_isSettingCurrentCell=true;//调用基类方法base.SetCurrentCellAddressCore(columnIndex,rowIndex,setAnchorCellAddress,validateCurrentCell,throughMouseClick);}finally{_isSettingCurrentCell=false;}}//可选:提供一个安全设置当前单元格的方法publicboolSafeSetCurrentCell(introwIndex,intcolumnIndex){if(_isSettingCurrentCell)returnfalse;if(rowIndex<0||rowIndex>=RowCount||columnIndex<0||columnIndex>=ColumnCount)returnfalse;CurrentCell=this[columnIndex,rowIndex];returntrue;}}```使用说明:1.在窗体设计器中,使用CustomDataGridView替换原来的DataGridView。2.在代码中需要设置当前单元格的地方,使用SafeSetCurrentCell方法而不是直接设置CurrentCell属性。或者,即使直接设置CurrentCell属性,由于重写了SetCurrentCellAddressCore,也会避免重入。注意事项:-在重写SetCurrentCellAddressCore时,我们使用了try/finally来确保标志变量在方法退出时被重置,即使发生异常。-在SafeSetCurrentCell方法中,我们检查了行列索引的有效性,避免无效设置。此方案通过标志变量阻止了重入调用,从而避免了堆栈溢出和状态不一致的问题。引用说明:我们参考了引用[2]中关于重写DataGridView方法的思想,特别是提到了重写SetCurrentCellAddressCore方法[^2]。§§相关问题§§1.什么是可重入调用(Reentrancy)?在编程中有什么危害?2.除了使用标志变量,还有哪些方法可以避免可重入问题?3.在DataGridView中,还有哪些常见的问题需要重写方法来解决?</think>### DataGridView 设置 CurrentCell 引发 SetCurrentCellAddressCore 可重入调用问题解决方案 #### 问题分析 当使用 `dgv.CurrentCell = cell` 设置当前单元格时,会触发 `SetCurrentCellAddressCore` 方法。如果在此方法执行过程中再次尝试设置当前单元格,会导致**可重入调用**(reentrant call),即方法在执行过程中被自身再次调用,这会引发不可预期的行为或异常[^2]。 #### 解决方案 以下是两种有效的解决方式,核心思想是**避免在设置过程中重复触发设置操作**: --- ### 方案一:使用标志位阻止重入(推荐) ```csharp public class SafeDataGridView : DataGridView { private bool _isSettingCell = false; // 重入保护标志位 protected override void SetCurrentCellAddressCore(int columnIndex, int rowIndex, bool setAnchorCellAddress, bool validateCurrentCell, bool throughMouseClick) { if (_isSettingCell) return; // 阻止重入调用 try { _isSettingCell = true; base.SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick); } finally { _isSettingCell = false; // 确保标志位重置 } } } ``` **使用方式:** 1. 将原 `DataGridView` 替换为 `SafeDataGridView` 2. 正常使用 `dgv.CurrentCell = cell` 设置单元格 3. 标志位会自动拦截重入调用 --- ### 方案二:通过 WinAPI 安全设置焦点 ```csharp [DllImport("user32.dll")] private static extern bool SetFocus(IntPtr hWnd); public static void SafeSetCurrentCell(DataGridView dgv, DataGridViewCell cell) { if (dgv.InvokeRequired) // 跨线程调用处理 { dgv.Invoke((Action)(() => SafeSetCurrentCell(dgv, cell))); return; } // 先移除焦点再设置单元格 SetFocus(IntPtr.Zero); dgv.CurrentCell = cell; dgv.Focus(); } ``` **使用方式:** ```csharp // 代替 dgv.CurrentCell = cell SafeSetCurrentCell(dataGridView1, dataGridView1[2, 3]); ``` --- ### 关键注意事项 1. **线程安全**:在跨线程操作时使用 `Control.Invoke` 2. **单元格有效性检查**:设置前需验证 ```csharp if (rowIndex >= 0 && rowIndex < RowCount && columnIndex >= 0 && columnIndex < ColumnCount) { // 安全设置代码 } ``` 3. **事件处理**:避免在 `CellBeginEdit` 等事件中直接修改当前单元格[^1] 4. **性能影响**:高频操作时方案一性能更优 > 两种方案均通过控制执行流避免重入调用。方案一更符合面向对象设计,方案二提供直接 API 级控制[^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值