代码之家  ›  专栏  ›  技术社区  ›  Adam Lear

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

  •  4
  • Adam Lear  · 技术社区  · 16 年前

    在我从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事件,发现在这个场景中,它仍然认为自己有一行,并尝试从绑定源中检索当前项,但是 m_bindingSource.Current

    6 回复  |  直到 16 年前
        1
  •  2
  •   Henk Holterman    16 年前

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

        2
  •  2
  •   Tyanna    16 年前

    经过一番挣扎,我发现了一些好消息和一些坏消息:

    好消息是 (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方法的第一行之前,一切都按计划进行。

    希望对你有帮助。

        3
  •  2
  •   Andy    14 年前

    我今天遇到了同样的问题,在这个线程中找到了解决方法。不幸的是,我不喜欢分割按钮的启用/禁用代码。所以我做了更多的研究,找到了另一个解决方案,这对我很有效。

    解决IndexOutOfRangeException所做的只是在设置按钮的enable/disable之前重置绑定。(为了优化性能,您可以检查数据源计数是零还是货币管理器的位置是-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);
    }
    

    希望对你有帮助。

        4
  •  1
  •   Adam Lear    16 年前

    我最终解决了这个问题:

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

    这有点奇怪,但似乎运作良好。

        5
  •  0
  •   Ivan Ferić    16 年前

    private void HandleBindingSourceCurrentChanged(object _sender, EventArgs _e)
        {
            if (m_bindingSource.Position < 0) return;
    
            btnRemove.Enabled = (m_bindingSource.Current != null);
    
        }
    
        6
  •  0
  •   Dennis Wallen    10 年前

    我认为出现这个问题是因为您禁用了当前具有焦点的按钮。禁用聚焦控件应该没有错,但在某些情况下,它会产生所描述的问题。如果你先把焦点放在其他控件上,我想你会发现问题消失了。我也有同样的问题,这对我很有效。

      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
    
    推荐文章