Проблема привязки DataGridView: «У индекса -1 нет значения». - PullRequest
4 голосов
/ 10 сентября 2010

У меня есть сетка данных, привязанная к источнику привязки, и пара кнопок на форме. Одна кнопка добавляет элемент к источнику привязки, другая удаляет выбранный в данный момент элемент. Также имеется обработчик событий, который прослушивает событие CurrentChanged и обновляет состояние «Включено» кнопки «Удалить».

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

   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, то все будет работать нормально. Так что я подозреваю что-то вроде порядка операций. Но что? Почему попытки внести изменения, совершенно не связанные с представлением данных, вызывают проблемы?

Чтобы сделать вещи еще более интересными, исключение обычно безвредно (или вообще не отображается), если программа запускается в VS с подключенным отладчиком. Но если он выполняется сам по себе, появляется окно с информацией об исключении.

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

Любая помощь будет принята с благодарностью. Спасибо.

Ответы [ 6 ]

2 голосов
/ 19 марта 2012

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

Все, что я сделал, чтобы разрешить исключение 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);
}

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

2 голосов
/ 13 сентября 2010

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

Хорошей новостью является то, что часть (m_bindingSource.Current != null); не является проблемой. Это работает очень хорошо.

Плохая новость заключается в том, что ошибка вызвана 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, все будет работать как запланировано.

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

2 голосов
/ 11 сентября 2010

Возможно, это не реальный ответ, но я помню, что BindingSource и Datagrid были разборчивы и хрупки в этом отделе. Мой общий совет - не использовать RemoveCurrent, а удалять запись из основного хранилища данных.

1 голос
/ 13 сентября 2010

Я закончил так:

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

Это немного странно, но, кажется, работает нормально.

0 голосов
/ 25 января 2016

Я думаю, что проблема возникает, потому что вы отключаете кнопку, которая в данный момент имеет фокус.Не должно быть ничего плохого в отключении сфокусированного управления, но в определенных обстоятельствах это приводило к описанной проблеме.Если вы сначала установите фокус на какой-то другой элемент управления, я думаю, что проблема исчезнет.У меня была такая же проблема, и она работала для меня.

  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 голосов
/ 11 сентября 2010

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

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

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

    }
...