DataGridView绑定问题:“索引-1没有值。”

时间:2010-09-10 19:47:28

标签: c# winforms data-binding datagridview .net-2.0

我有一个datagridview绑定到绑定源和窗体上的几个按钮。一个按钮将一个项目添加到绑定源,另一个按钮删除当前选定的项目。还有一个事件处理程序,它侦听CurrentChanged事件并更新“删除”按钮的“已启用”状态。

在我从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中启动且附加了调试器,则异常通常是无害的(或根本不显示)。但如果它自己执行,会弹出一个带有异常细节的消息框。

我尝试在datagridview上处理RowEnter事件,发现在这种情况下,它仍然认为它有一行并尝试从绑定源中检索Current项,但m_bindingSource.Current已经为空。为什么在处理CurrentChanged事件时这只是一个问题?

非常感谢任何和所有帮助。感谢。

6 个答案:

答案 0 :(得分:2)

也许不是一个真正的答案,但我记得BindingSource和Datagrid在这个部门是挑剔和脆弱的。我的一般建议是不使用RemoveCurrent,而是从基础数据存储中删除记录。

答案 1 :(得分:2)

经过一番讽刺后,我发现了一些好消息和一些坏消息:

好消息是(m_bindingSource.Current != null);部分不是问题所在。运行得很好。

坏消息是错误是由btnRemove.Enabled = false;

引起的

看看我的意思,改变:btnRemove.Enabled = (m_bindingSource.Current != null); 致:

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

代码将在第一行死亡。

我不是百分之百确定原因,但是如果你将btnRemove.Enabled = false移到HandleRemoveClick方法的第一行,一切都按计划进行。

希望这对你有所帮助。

答案 2 :(得分: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);
}

希望这有帮助。

答案 3 :(得分: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;
}

这有点奇怪,但似乎工作正常。

答案 4 :(得分:0)

尝试用以下代码替换CurrentChanged处理程序:

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

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

    }

答案 5 :(得分: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