最近接到客戶回報一個例外狀況 :
System.InvalidOperationException: 作業無效,因為它會產生對 SetCurrentCellAddressCore 函式的可重新進入呼叫。
於 System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
於 System.Windows.Forms.DataGridView.set_CurrentCell(DataGridViewCell value)
於 System.Windows.Forms.DataGridView.OnBindingContextChanged(EventArgs e)
於 System.Windows.Forms.Control.CreateControl()
於 System.Windows.Forms.Control.WmShowWindow(Message& m)
於 System.Windows.Forms.Control.WndProc(Message& m)
於 System.Windows.Forms.DataGridView.WndProc(Message& m)
於 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
於 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
原本以為是設定 DataGridView.CurrentCell = xxx => 引發事件 => 在處理函式中又重覆設定 DataGridView.CurrentCell 造成的, 於是全面搜尋 source code 找出有設定 CurrentCell 的程式碼卻沒有發現上述的情形, 經同事指出發生這種例外的一種狀況是 "以對話框顯示 DataGridView 供使用者編輯資料, 使用者在 DataGridView 某一種自行開發的 DataGridViewColumn 還是編輯模式下直接按"X"關閉對話框, 下次再顯示這個對話框前清除 DataGridView 繫結的舊資料" 時就會出現, 後來我又測出不管是何種型態的 DataGridViewColumn, 只要是在 DataGridView 還在編輯模式下關閉對話框也會發生, 於是寫了一個簡單的程式模擬這個狀況 :
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Data;
namespace WindowsApplication1
{
static class Program
{
/// <summary>
/// 應用程式的主要進入點。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
class MainForm : Form
{
public MainForm()
{
//to display Exception
textBox = new TextBox();
textBox.Multiline = true;
textBox.ReadOnly = true;
textBox.ScrollBars = ScrollBars.Both;
textBox.Dock = DockStyle.Fill;
this.Controls.Add(textBox);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
showdlg(); //ok
showdlg(); //catch a Exception
}
TextBox textBox;
DataGridViewDialog dlg;
void showdlg()
{
if (dlg == null) //create a dialog with DataGridView
{
dlg = new DataGridViewDialog();
dlg.Shown += new EventHandler(dlg_Shown);
dlg.Disposed += new EventHandler(dlg_Disposed);
}
try { dlg.ClearData(); } catch (Exception exp) { appendExceptionMsg(exp); }
dlg.ShowDialog();
}
void appendExceptionMsg(Exception exp)
{
textBox.AppendText(exp.GetType().FullName + ":");
textBox.AppendText(exp.Message + "\r\n");
if (exp.TargetSite != null)
textBox.AppendText(exp.TargetSite.Name + "\r\n");
textBox.AppendText(exp.StackTrace);
if (exp.InnerException != null)
{
textBox.AppendText("\r\n\r\n");
appendExceptionMsg(exp.InnerException);
}
}
void dlg_Disposed(object sender, EventArgs e)
{
dlg = null;
}
void dlg_Shown(object sender, EventArgs e)
{
dlg.Close();
}
}
class DataGridViewDialog : Form
{
public DataGridViewDialog()
{
dataGridView1 = new DataGridView();
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter; //let DataGridView in edit mode
this.Controls.Add(dataGridView1);
table = new DataTable();
table.Columns.Add("C1", typeof(string));
table.Columns.Add("C2", typeof(string));
for (int i = 0; i < 2; i++)
table.Rows.Add(new object[] { i });
bs = new BindingSource();
bs.DataSource = table;
dataGridView1.DataSource = bs;
}
public void ClearData()
{
table.Rows.Clear();
}
//protected override void OnFormClosed(FormClosedEventArgs e)
//{
// base.OnFormClosed(e);
// this.dataGridView1.CurrentCell = null;
//}
DataTable table;
BindingSource bs;
DataGridView dataGridView1;
}
}
結果這次抓到的是 :
System.NullReferenceException:並未將物件參考設定為物件的執行個體
get_MouseOverEditingControl
於 System.Windows.Forms.DataGridView.get_MouseOverEditingControl()
於 System.Windows.Forms.DataGridView.EndEdit(DataGridViewDataErrorContexts context, DataGridViewValidateCellInternal validateCell, Boolean fireCellLeave, Boolean fireCellEnter, Boolean fireRowLeave, Boolean fireRowEnter, Boolean fireLeave, Boolean keepFocus, Boolean resetCurrentCell, Boolean resetAnchorCell)
於 System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
於 System.Windows.Forms.DataGridView.set_CurrentCell(DataGridViewCell value)
於 System.Windows.Forms.DataGridView.OnClearingRows()
於 System.Windows.Forms.DataGridViewRowCollection.ClearInternal(Boolean recreateNewRow)
於 System.Windows.Forms.DataGridView.RefreshRows(Boolean scrollIntoView)
於 System.Windows.Forms.DataGridView.DataGridViewDataConnection.ProcessListChanged(ListChangedEventArgs e)
於 System.Windows.Forms.DataGridView.DataGridViewDataConnection.currencyManager_ListChanged(Object sender, ListChangedEventArgs e)
於 System.Windows.Forms.CurrencyManager.OnListChanged(ListChangedEventArgs e)
於 System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
於 System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
於 System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e)
於 System.Data.DataView.OnListChanged(ListChangedEventArgs e)
於 System.Data.DataView.IndexListChanged(Object sender, ListChangedEventArgs e)
於 System.Data.DataView.IndexListChangedInternal(ListChangedEventArgs e)
於 System.Data.DataViewListener.IndexListChanged(ListChangedEventArgs e)
於 System.Data.Index.<OnListChanged>b__2(DataViewListener listener, ListChangedEventArgs args, Boolean arg2, Boolean arg3)
於 System.Data.Listeners`1.Notify[T1,T2,T3](T1 arg1, T2 arg2, T3 arg3, Action`4 action)
於 System.Data.Index.OnListChanged(ListChangedEventArgs e)
於 System.Data.Index.FireResetEvent()
於 System.Data.Index.Reset()
於 System.Data.DataTable.ResetInternalIndexes(DataColumn column)
於 System.Data.DataTable.Clear(Boolean clearAll)
於 System.Data.DataRowCollection.Clear()
於 WindowsApplication1.DataGridViewDialog.ClearData() 於 C:\Users\Marlon.Wu\AppData\Local\Temporary Projects\WindowsApplication1\Program.cs: 行 114
於 WindowsApplication1.MainForm.showdlg() 於 C:\Users\Marlon.Wu\AppData\Local\Temporary Projects\WindowsApplication1\Program.cs: 行 56
關閉對話框前把 DataGridView.CurrentCell 設為 null 可以避免這種例外狀況