DataGridView binding problem: "Index -1 doesn't matter."

I have a datagridview associated with a binding source and a couple of buttons in a form. One button adds an item to the binding source, and the other removes the selected item. There is also an event handler that listens for the CurrentChanged event and updates the Enabled status of the Remove button.

All this is hunky dory until I leave to remove the last item from datagridview. Then I see a very ugly exception:

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) 

I highlighted the problem in a small scenario:

  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; } } 

Thanks to pure experimentation, I found that if I did not change the state of the button in the CurrentChanged event handler, everything would be fine. Therefore, I suspect some order of operations. But what? Why is trying to make changes completely unrelated to the datagridview problem causing problems?

To make things even more interesting, an exception is usually harmless (or not displayed at all) if the program is running in VS with a debugger attached. But if it runs by itself, a window appears with an exception message.

I tried to handle the RowEnter event in a datagridview and found that in this case it still considers that it has a row and is trying to extract the current element from the binding source, but m_bindingSource.Current already null. Why is this only a problem when handling the CurrentChanged event?

Any help is appreciated. Thanks.

+4
source share
6 answers

I ended up resolving this:

 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; } 

This is a bit weird, but seems to work fine.

+1
source

This may not be the real answer, but I remember that BindingSource and Datagrid were picky and fragile in this section. My general advice would not be to use RemoveCurrent, but to delete a record from the underlying data store.

+2
source

After some figure, I found some good news and some bad news for you:

The good news is that the part (m_bindingSource.Current != null); no problem. This works great.

The bad news is that the error is caused by btnRemove.Enabled = false;

Look what I mean, change: btnRemove.Enabled = (m_bindingSource.Current != null); To:

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

The code will die on the first line.

I'm not 100% sure, but if you move btnRemove.Enabled = false to the first line of the HandleRemoveClick method, everything will work as planned.

I hope you find it helpful.

+2
source

Today I ran into the same problem and found a workaround in this thread. Unfortunately, I did not like to split the on / off code of the buttons. So I did some more research and found another solution that worked for me.

All I did to resolve the IndexOutOfRangeException was to reset the bindings before I set the buttons on / off. (To optimize performance, you can check if the data counter is zero or the position of the currency manager is -1. In other cases, reset is not required.)

 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); } 

Hope this helps.

+2
source

Try replacing the CurrentChanged handler with:

 private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e) { if (m_bindingSource.Position < 0) return; btnRemove.Enabled = (m_bindingSource.Current != null); } 
0
source

I think the problem arises because you disabled the button that currently has focus. There should be nothing wrong with disabling the focused control, but under certain circumstances it creates the described problem. If you first set the focus to some other control, I think you will see that the problem goes away. I had the same issue and it worked for me.

  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 
0
source

All Articles