2010-09-10 2 views
4

У меня есть datagridview, связанный с источником привязки и пару кнопок на форме. Одна кнопка добавляет элемент в источник привязки, а другой удаляет выбранный элемент. Также есть обработчик событий, который прослушивает событие CurrentChanged и обновляет статус Enabled кнопки Remove.Проблема привязки DataGridView: «Индекс -1 не имеет значения».

Все, что вам нравится, пока я не уйду, чтобы удалить последний элемент из datagridview. Потом я вижу очень некрасиво исключение:

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 с прикрепленным отладчиком. Но если он выполняется сам по себе, появляется окно с сообщением об исключении.

Я попытался обработать событие RowEnter в datagridview и обнаружил, что в этом случае он все еще считает, что он имеет строку и пытается извлечь текущий элемент из источника привязки, но m_bindingSource.Current уже имеет значение null. Почему это только проблема при обработке события CurrentChanged?

Любая помощь была бы принята с благодарностью. Благодарю.

+1

Вы действительно проверили, что это Button.Enabled и _not_ чтение BindSource.Current это критически важно? –

+0

@ Хенк: похоже. Я разделил код установки Enabled на две строки: «var currentIsNotNull = m_bindingSource.Current! = Null; btnRemove.Enabled = currentIsNotNull;». Затем исключение вызывается установщиком btnRemove.Enabled. Тем не менее, если я вообще не основываю значение свойства Enabled на источнике привязки, все работает нормально, поэтому, возможно, это комбинация чтения и установщика Enabled. –

+0

Я пробовал ваш код, и он работает отлично. Нет проблем, никаких исключений из отладчика визуальной студии и непосредственно из .exe. ... – pdiddy

ответ

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

Это похоже на трюк. Благодаря! –

+0

... Или нет. Возможно, настало время укусить пулю и переписать ее, чтобы полностью исключить источник связывания. –

+0

Пока вы кусаете, рассмотрите WPF и MVVM (: –

0

Попробуйте заменить обработчик CurrentChanged с:

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

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

    } 
+0

Не могу вернуться - это позволит включить кнопку. Но я постараюсь идти по позиции вместо проверки Тока. Благодаря! –

2

После некоторого figgling, я обнаружил некоторые хорошие новости и плохие новости для вас:

Хорошая новость заключается в том, что (m_bindingSource.Current != null); часть ISN» Проблема. Это нормально.

Плохая новость заключается в том, что ошибка была вызвана btnRemove.Enabled = false;

Видят, что я имею в виду, изменение: btnRemove.Enabled = (m_bindingSource.Current != null); To:

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

Код будет умирать на первой линии.

Я не на 100% уверен, почему, но если вы переместите btnRemove.Enabled = false до первой строки метода HandleRemoveClick, все будет работать так, как планировалось.

Надеюсь, это полезно для вас.

+0

Это полезно. См. Мой ответ для окончательного решения. Благодаря! –

2

Я столкнулся с той же проблемой сегодня и нашел обходной путь в этой теме. К сожалению, мне не понравилось разделить код включения/выключения кнопок. Поэтому я сделал еще несколько исследований и нашел другое решение, которое сработало для меня.

Все, что я сделал для устранения параметра IndexOutOfRangeException, было сбросом привязок, прежде чем я установил включение/выключение кнопок. (Для того, чтобы оптимизировать производительность, вы можете проверить, если количество DataSource равно нуль или позиция менеджера валюты является -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 
Смежные вопросы