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

发布于 2024-09-18 19:55:35 字数 4134 浏览 4 评论 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 事件,发现在这种情况下,它仍然认为它有一行并尝试从绑定源检索当前项目,但 m_bindingSource.Current 已经无效的。为什么只有在处理 CurrentChanged 事件时才会出现这个问题?

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

I have a datagridview bound to a binding source and a couple buttons on a form. One button adds an item to the binding source, the other removes the currently selected item. There's also an event handler that listens to the CurrentChanged event and updates the Enabled status of the Remove button.

Everything is hunky dory until I go to remove the last item from the datagridview. Then I see a very ugly exception:

   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)

I've isolated the problem in a small scenario:

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

Through pure experimentation, I've found that if I don't alter the button state in the CurrentChanged event handler, then everything works fine. So I suspect some sort of order of operations issue. But what? Why does attempting to make a change completely unrelated to the datagridview cause issues?

To make things even more interesting, the exception is usually harmless (or not showing up at all) if the program is started within VS with a debugger attached. But if it's executed on its own, there's a message box popping up with exception details.

I've tried handling the RowEnter event on the datagridview and found that in this scenario, it still thinks it has a row and attempts to retrieve the Current item from the binding source, but m_bindingSource.Current is already null. Why is this only an issue when the CurrentChanged event is handled?

Any and all help would be greatly appreciated. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

五里雾 2024-09-25 19:55:35

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

Maybe not a real answer but I remember BindingSource and Datagrid being picky and brittle in this department. My general advice would be not to use RemoveCurrent but to delete the record from the underlying datastore.

樱&纷飞 2024-09-25 19:55:35

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

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

坏消息是,该错误是由 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 方法的第一行,一切都会按计划进行。

希望这对您有帮助。

After some figgling, I've discovered some good news and some bad news for you:

The good news is that the (m_bindingSource.Current != null); part isn't the problem. That runs just fine.

The bad news is that the error is being caused by btnRemove.Enabled = false;

Do see what I mean, change: btnRemove.Enabled = (m_bindingSource.Current != null);
To:

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

The code will die on the first line.

I'm not 100% sure why, but if you move btnRemove.Enabled = false up to the first line of the HandleRemoveClick method everything works as planned.

Hope that's helpful to you.

你怎么这么可爱啊 2024-09-25 19:55:35

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

我为解决 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);
}

希望这有帮助。

I ran into the same problem today and found the workaround in this thread. Unfortunately I didn't like to split up the enable/disable code of the buttons. So I did some more research and found another solution, which worked for me.

All I did to resolve the IndexOutOfRangeException was to reset the bindings before I set the enable/disable of the buttons. (To optimize the performance you may check if the datasource count is zero or the position of the currency manager is -1. In other cases the reset isn't necessary.)

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

Hope that's helpful.

困倦 2024-09-25 19:55:35

我最终是这样解决的:

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

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

I ended up resolving it like this:

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

It's a little weird, but seems to be working fine.

木落 2024-09-25 19:55:35

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

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

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

    }

Try replacing the CurrentChanged handler with:

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

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

    }
凉世弥音 2024-09-25 19:55:35

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

  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

I think the problem occurs because you are disabling a button that currently has the focus. There should be nothing wrong with disabling the focused control, but in certain circumstances it produced the described problem. If you the set the focus to some other control first I think you will see the problem go away. I was having the same problem and it worked for me.

  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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文