2010-09-10 71 views
4

我有一个datagridview绑定到一个绑定源和一个窗体上的几个按钮。一个按钮将一个项目添加到绑定源,另一个按钮删除当前选定的项目。还有一个事件处理程序,用于侦听CurrentChanged事件并更新Remove按钮的Enabled状态。DataGridView绑定问题:“索引-1没有值。”

直到我去掉datagridview中的最后一项时,所有东西都是hunky dory。然后我看到一个很丑陋的例外:

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已经为null。为什么在处理CurrentChanged事件时这只是一个问题?

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

+1

您是否真的验证过它是Button.Enabled和_not_读取BindSource.Current是至关重要的? – 2010-09-10 19:53:26

+0

@亨克:看起来如此。我将Enabled设置代码分成两行:“var currentIsNotNull = m_bindingSource.Current!= null; btnRemove.Enabled = currentIsNotNull;”。然后由btnRemove.Enabled设置器抛出异常。也就是说,如果我根本没有将Enabled属性的值设置在绑定源上,那么一切运行良好,所以也许它是read和Enabled setter的组合。 – 2010-09-10 20:07:37

+0

我试过你的代码,它看起来很完美。没有问题,直接从Visual Studio调试器和.exe没有例外。 ... – pdiddy 2010-09-10 20:22:43

回答

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

这是一个有点怪异,但似乎工作的罚款。

2

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

+0

这看起来确实有用。谢谢! – 2010-09-10 20:37:04

+0

...或不。这可能是时候咬紧牙关,重写这个来彻底消除绑定来源。 – 2010-09-10 20:45:05

+0

当你咬人的时候,考虑WPF和MVVM(: – 2010-09-10 21:06:41

0

尝试用替换CurrentChanged处理程序:

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

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

    } 
+0

无法返回 - 这会让按钮启用。但我会尝试按位置而不是检查当前。谢谢! – 2010-09-11 03:12:17

2

一些figgling之后,我发现了一些好消息和一个坏消息要告诉你:

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

坏消息是,错误是由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方法的第一行,则所有工作都按计划进行。

希望对你有帮助。

+0

这很有帮助。查看我的答案以获得最终解决方案。谢谢! – 2010-09-13 16:01:25

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

希望这是有帮助的。

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