2010-09-10 62 views
4

我有一個datagridview綁定到一個綁定源和一個窗體上的幾個按鈕。一個按鈕將一個項目添加到綁定源,另一個按鈕刪除當前選定的項目。還有一個事件處理程序,用於偵聽CurrentChanged事件並更新Remove按鈕的Enabled狀態。DataGridView綁定問題:「索引-1沒有值。」

直到我去掉datagridview中的最後一項時,所有東西都是hunky dory。然後我看到一個很醜陋的例外:

at System.Windows.Forms.CurrencyManager.get_Item(Int32 index) 
    at System.Windows.Forms.CurrencyManager.get_Current() 
    at System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnRowEnter(DataGridViewCellEventArgs e) 
    at System.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell& dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred) 
    at System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick) 
    at System.Windows.Forms.DataGridView.SetAndSelectCurrentCellAddress(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick, Boolean clearSelection, Boolean forceCurrentCellSelection)\r\n at System.Windows.Forms.DataGridView.MakeFirstDisplayedCellCurrentCell(Boolean includeNewRow) 
    at System.Windows.Forms.DataGridView.OnEnter(EventArgs e) 
    at System.Windows.Forms.Control.NotifyEnter() 
    at System.Windows.Forms.ContainerControl.UpdateFocusedControl() 
    at System.Windows.Forms.ContainerControl.AssignActiveControlInternal(Control value) 
    at System.Windows.Forms.ContainerControl.ActivateControlInternal(Control control, Boolean originator) 
    at System.Windows.Forms.ContainerControl.SetActiveControlInternal(Control value) 
    at System.Windows.Forms.ContainerControl.SetActiveControl(Control ctl) 
    at System.Windows.Forms.ContainerControl.set_ActiveControl(Control value) 
    at System.Windows.Forms.Control.Select(Boolean directed, Boolean forward) 
    at System.Windows.Forms.Control.SelectNextControl(Control ctl, Boolean forward, Boolean tabStopOnly, Boolean nested, Boolean wrap) 
    at System.Windows.Forms.Control.SelectNextControlInternal(Control ctl, Boolean forward, Boolean tabStopOnly, Boolean nested, Boolean wrap) 
    at System.Windows.Forms.Control.SelectNextIfFocused() 
    at System.Windows.Forms.Control.set_Enabled(Boolean value) 
    at Bug3324.Form1.HandleBindingSourceCurrentChanged(Object _sender, EventArgs _e) in D:\\Dev\\TempApps\\Bug3324\\Bug3324\\Form1.cs:line 41 
    at System.Windows.Forms.BindingSource.OnCurrentChanged(EventArgs e) 
    at System.Windows.Forms.BindingSource.CurrencyManager_CurrentChanged(Object sender, EventArgs e) 
    at System.Windows.Forms.CurrencyManager.OnCurrentChanged(EventArgs e) 

我隔離在一個小場景的問題:

private BindingSource m_bindingSource = new BindingSource(); 

    public Form1() 
    { 
     InitializeComponent(); 

     m_bindingSource.CurrentChanged += HandleBindingSourceCurrentChanged; 
     m_bindingSource.DataSource = new BindingList<StringValue>(); 

     dataGridView1.DataSource = m_bindingSource; 

     btnAdd.Click += HandleAddClick; 
     btnRemove.Click += HandleRemoveClick; 
    } 

    private void HandleRemoveClick(object _sender, EventArgs _e) 
    { 
     m_bindingSource.RemoveCurrent(); 
    } 

    private void HandleAddClick(object _sender, EventArgs _e) 
    { 
     m_bindingSource.Add(new StringValue("Some string")); 
    } 

    private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e) 
    { 
     // this line throws an exception when the last item is removed from 
     // the datagridview 
     btnRemove.Enabled = (m_bindingSource.Current != null); 

    } 
} 

public class StringValue 
{ 
    public string Value { get; set; } 

    public StringValue(string value) 
    { 
     Value = value; 
    } 
} 

通過純粹的實驗,我發現,如果我不改變按鈕狀態在CurrentChanged事件處理程序中,然後一切工作正常。所以我懷疑某種操作順序問題。但是什麼?爲什麼試圖與datagridview完全無關的更改會導致問題?

爲了使事情更加有趣,如果程序在附帶調試器的情況下在VS中啓動,則通常無害(或根本不顯示)。但如果它自己執行,則會彈出一個消息框,其中包含異常詳細信息。

我試着處理datagridview上的RowEnter事件,發現在這種情況下,它仍然認爲它有一行並試圖從綁定源中檢索Current項,但m_bindingSource.Current已經爲null。爲什麼在處理CurrentChanged事件時這只是一個問題?

任何和所有的幫助將不勝感激。謝謝。

+1

您是否真的驗證過它是Button.Enabled和_not_讀取BindSource.Current是至關重要的? – 2010-09-10 19:53:26

+0

@亨克:看起來如此。我將Enabled設置代碼分成兩行:「var currentIsNotNull = m_bindingSource.Current!= null; btnRemove.Enabled = currentIsNotNull;」。然後由btnRemove.Enabled設置器拋出異常。也就是說,如果我根本沒有將Enabled屬性的值設置在綁定源上,那麼一切運行良好,所以也許它是read和Enabled setter的組合。 – 2010-09-10 20:07:37

+0

我試過你的代碼,它看起來很完美。沒有問題,直接從Visual Studio調試器和.exe沒有例外。 ... – pdiddy 2010-09-10 20:22:43

回答

1

我最終解決這樣的:

private void HandleRemoveClick(object _sender, EventArgs _e) 
{ 
    btnRemove.Enabled = false; 
    m_bindingSource.RemoveCurrent(); 
} 

private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e) 
{ 
    if(m_bindingSource.Current != null) 
     btnRemove.Enabled = true; 
} 

這是一個有點怪異,但似乎工作的罰款。

2

也許不是一個真正的答案,但我記得BindingSource和Datagrid在這個部門中挑剔和脆弱。我的一般建議是不使用RemoveCurrent,而是從底層數據存儲中刪除記錄。

+0

這看起來確實有用。謝謝! – 2010-09-10 20:37:04

+0

...或不。這可能是時候咬緊牙關,重寫這個來徹底消除綁定來源。 – 2010-09-10 20:45:05

+0

當你咬人的時候,考慮WPF和MVVM(: – 2010-09-10 21:06:41

0

嘗試用替換CurrentChanged處理程序:

private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e) 
    { 
     if (m_bindingSource.Position < 0) return; 

     btnRemove.Enabled = (m_bindingSource.Current != null); 

    } 
+0

無法返回 - 這會讓按鈕啓用。但我會嘗試按位置而不是檢查當前。謝謝! – 2010-09-11 03:12:17

2

一些figgling之後,我發現了一些好消息和一個壞消息要告訴你:

好消息是,(m_bindingSource.Current != null);部分ISN」問題。這運行得很好。

壞消息是,錯誤是由btnRemove.Enabled = false;

造成難道明白我的意思,改:btnRemove.Enabled = (m_bindingSource.Current != null); 要:

btnRemove.Enabled = false; 
if(m_bindingSource.Current != null) 
    btnRemove.Enabled = true; 

的代碼將在第一行死亡。

我不是100%確定爲什麼,但如果您將btnRemove.Enabled = false移動到HandleRemoveClick方法的第一行,則所有工作都按計劃進行。

希望對你有幫助。

+0

這很有幫助。查看我的答案以獲得最終解決方案。謝謝! – 2010-09-13 16:01:25

2

今天我遇到了同樣的問題,並在此線程中找到了解決方法。不幸的是,我不喜歡拆分按鈕的啓用/禁用代碼。所以我做了一些更多的研究,並找到了另一個解決方案,這對我來說很合適

我所做的解決IndexOutOfRangeException的問題是在設置按鈕的啓用/禁用之前重置綁定。 (爲了優化您可以檢查數據源計數爲零或貨幣經理的位置爲-1的性能。在其他情況下復位是沒有必要的。)

private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e) 
{ 
    if(m_bindingSource.Count == 0) // You also can check position == -1 
    { 
     m_bindingSource.ResetBindings(false); 
    } 

    btnRemove.Enabled = (m_bindingSource.Current != null); 
} 

希望這是有幫助的。

0

我認爲問題的發生是因爲您正在禁用當前具有焦點的按鈕。禁用集中控制應該沒有問題,但在某些情況下會產生所描述的問題。如果你先把焦點設置在其他控件上,我想你會看到問題消失。我遇到了同樣的問題,併爲我工作。

Dim bCurrent As Boolean = CredentialTypeBindingSource.Current IsNot Nothing 
    'set focus to the New button which is never disabled 
    NewBtn.Focus() 
    'enable/disable the other buttons 
    EditBtn.Enabled = bCurrent 
    DeleteBtn.Enabled = bCurrent