【问题标题】:DataGridView binding problem: "Index -1 does not have a value."DataGridView 绑定问题:“索引 -1 没有值。”
【发布时间】:2011-04-10 21:51:30
【问题描述】:

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

在我从 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 已经为空。为什么这只是处理 CurrentChanged 事件时的问题?

我们将不胜感激任何和所有的帮助。谢谢。

【问题讨论】:

  • 您是否真的验证了它是 Button.Enabled 而而不是 BindSource.Current 的读数至关重要?
  • @Henk:看起来是这样。我将 Enabled 设置代码分成两行:“var currentIsNotNull = m_bindingSource.Current != null; btnRemove.Enabled = currentIsNotNull;”。然后由 btnRemove.Enabled 设置器抛出异常。也就是说,如果我根本不将 Enabled 属性的值基于绑定源,那么一切运行正常,所以它可能是 read 和 Enabled setter 的组合。
  • 我试过你的代码,它似乎工作得很好。没问题,Visual Studio 调试器和直接从 .exe 也不例外。 ...
  • @pdiddy:不幸的是,这并不让我感到惊讶。这对我来说也很好用了几个月。

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


【解决方案1】:

我认为出现问题是因为您正在禁用当前具有焦点的按钮。禁用集中控制应该没有错,但在某些情况下,它会产生所描述的问题。如果您首先将焦点设置为其他控件,我认为您会看到问题消失。我遇到了同样的问题,它对我有用。

  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

【讨论】:

    【解决方案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】:

      我最终是这样解决的:

      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】:

        经过一番琢磨,我为你发现了一些好消息和一些坏消息:

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

        希望对你有帮助。

        【讨论】:

        • 这很有帮助。请参阅我的答案以获取最终解决方案。谢谢!
        【解决方案5】:

        尝试将 CurrentChanged 处理程序替换为:

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

        【讨论】:

        • 无法返回 - 这将使按钮保持启用状态。但我会尝试按位置而不是检查当前。谢谢!
        【解决方案6】:

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

        【讨论】:

        • ... 还是不行。可能是时候咬紧牙关重写它以完全消除绑定源了。
        • 当你咬牙切齿的时候,考虑一下 WPF 和 MVVM (:
        • 我不认为我可以完全由我的老板摇摆那个。 :)
        猜你喜欢
        • 2010-11-28
        • 1970-01-01
        • 2011-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多