DataGridViewComboBoxColumn二次点击触发System.NullReferenceException问题
DataGridViewComboBoxColumn二次点击触发NullReferenceException问题
我开发了一个Windows Form应用,主窗体里的DataGridView连接SQL Server数据库,其中一列是ComboBoxColumn。我通过EditingControlShowing事件把它转换成实际的ComboBox并绑定了多个事件:
private void dgrv1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { var cb = e.Control as DataGridViewComboBoxEditingControl; cb.SelectedIndexChanged -= this.cb_SelectedIndexChanged; cb.SelectedIndexChanged += this.cb_SelectedIndexChanged; cb.DropDown -= this.cb_DropDown; cb.DropDown += this.cb_DropDown; cb.DropDownClosed -= this.cb_DropDownClosed; cb.DropDownClosed += this.cb_DropDownClosed; } private void cb_DropDown (object sender, EventArgs e) { var cb = sender as DataGridViewComboBoxEditingControl; string text = cb.Text; } private void cb_SelectedIndexChanged(object sender, EventArgs e) { var cb = sender as DataGridViewComboBoxEditingControl; int ind = cb.SelectedIndex; dgrv1.CommitEdit(DataGridViewDataErrorContexts.Commit); dgrv1.EndEdit; // 语法错误:缺少方法调用括号 } private void cb_DropDownClosed(object sender, EventArgs e) { var cb = sender as DataGridViewComboBoxEditingControl; dgrv1.EndEdit; // 语法错误:缺少方法调用括号 }
首次点击该ComboBoxColumn时所有事件正常运行,但第二次点击时,无论是否修改选中项,都会在Program.cs的Application.Run行抛出System.NullReferenceException。禁用EditingControlShowing事件则不会出现此异常。异常堆栈信息如下:
System.NullReferenceException at System.Windows.Forms.DataGridView.InitializeEditingControlValue(DataGridViewCellStyle& dataGridViewCellStyle, DataGridViewCell dataGridViewCell) at System.Windows.Forms.DataGridView.BeginEditInternal(Boolean selectAll) at System.Windows.Forms.DataGridView.BeginEdit(Boolean selectAll) at System.Windows.Forms.DataGridViewComboBoxCell.OnMouseClick(DataGridViewCellMouseEventArgs e) at System.Windows.Forms.DataGridView.OnMouseClick(DataGridViewCellMouseEventArgs e) at System.Windows.Forms.DataGridView.OnMouseClick(MouseEventArgs e) at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.DataGridView.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) at System.Windows.Forms.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.Run(Form mainForm) at MyProject.Program.Main() in <解决方案路径>\MyProject\Program.cs:line19
问题根源
- 事件管理不当:DataGridView的编辑控件是复用的,仅在
EditingControlShowing中绑定事件但未在编辑结束后解绑,导致旧事件处理程序关联到已失效的控件实例,重复触发时引发空引用。 - 编辑流程被强制中断:在
SelectedIndexChanged和DropDownClosed中提前调用EndEdit,干扰了DataGridView的内部编辑生命周期,第二次进入编辑时控件初始化失败。 - 语法错误:
dgrv1.EndEdit缺少方法调用括号,属于编译错误,会引发意外运行时行为。 - 空值判断缺失:控件转换时未做空值校验,一旦转换失败直接访问属性会触发空引用异常。
修复方案
1. 完整管理事件绑定与解绑
在EditingControlShowing中绑定事件,同时在CellEndEdit事件中解绑,避免复用控件残留旧引用:
private void dgrv1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { var cb = e.Control as DataGridViewComboBoxEditingControl; if (cb == null) return; // 增加空值判断 // 先解绑再绑定,防止重复绑定 cb.SelectedIndexChanged -= cb_SelectedIndexChanged; cb.SelectedIndexChanged += cb_SelectedIndexChanged; cb.DropDown -= cb_DropDown; cb.DropDown += cb_DropDown; cb.DropDownClosed -= cb_DropDownClosed; cb.DropDownClosed += cb_DropDownClosed; } private void dgrv1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { var cb = dgrv1.EditingControl as DataGridViewComboBoxEditingControl; if (cb == null) return; // 编辑结束后解绑事件 cb.SelectedIndexChanged -= cb_SelectedIndexChanged; cb.DropDown -= cb_DropDown; cb.DropDownClosed -= cb_DropDownClosed; }
2. 调整编辑提交逻辑
移除SelectedIndexChanged和DropDownClosed中的强制EndEdit调用,改用CellValueChanged处理值变更后的业务逻辑:
private void dgrv1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { // 仅处理ComboBox列的变更 if (dgrv1.Columns[e.ColumnIndex] is DataGridViewComboBoxColumn) { var selectedValue = dgrv1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; // 这里添加值变更后的处理逻辑 } } // 修改原SelectedIndexChanged方法,仅保留必要逻辑 private void cb_SelectedIndexChanged(object sender, EventArgs e) { var cb = sender as DataGridViewComboBoxEditingControl; if (cb == null) return; int ind = cb.SelectedIndex; // 不要在此处调用EndEdit/CommitEdit }
3. 修复语法错误
确保所有方法调用带有括号:
// 修正后的代码示例 dgrv1.EndEdit(); dgrv1.CommitEdit(DataGridViewDataErrorContexts.Commit);
4. 全局增加空值判断
所有涉及控件转换的方法都添加空值校验:
private void cb_DropDown(object sender, EventArgs e) { var cb = sender as DataGridViewComboBoxEditingControl; if (cb == null) return; string text = cb.Text; }
内容的提问来源于stack exchange,提问作者Anima
相关产品推荐
相关产品推荐

