为什么在循环中第二次删除列表框项会崩溃?

时间:2015-02-11 18:08:18

标签: c# for-loop listbox crash windows-ce

这是一个持续存在的问题;在下面的代码中,旧代码(也失败了)被注释掉了。不过,新代码的行为方式相同:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        //listBoxWork.DataSource = null; // <= This (at one time, anyway) seemed necessary to circumvent "Value does not fall within the expected range"
        //// Failing ignominiously; question at https://stackoverflow.com/questions/28439941/why-is-this-array-list-removal-code-failing
        //for (int i = listBoxWork.Items.Count - 1; i >= 0; --i) // try a foreach instead?
        //{
        //    if (listBoxWork.Items[i].ToString().Contains(listboxVal))
        //    {
        //        listBoxWork.Items.RemoveAt(i);
        //    }
        //}
        BindingSource bs = listBoxWork.DataSource as BindingSource;
        ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#2");
        for (int i = bs.Count - 1; i >= 0; --i)
        {
            if (bs[i].ToString().Contains(listboxVal))
            {
                bs.RemoveAt(i);
                ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#3");
            }
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}

似乎第二次通过for循环,应用程序突然崩溃。我这样说是因为日志文件显示&#34;到达frmMain.UpdateGUIAfterTableSend#3&#34;但不是来自catch块的log msg。因此它必须在四个循环的第二次迭代中崩溃(在&#34;之前达到frmMain.UpdateGUIAfterTableSend#3&#34;再次记录)。因此,一旦它崩溃了它的晚安,艾琳:它崩溃如此艰难和快速,它甚至没有记录捕获块的日志消息。还是有其他解释吗?

但更重要的是:为什么第二次检查&#34;包含&#34;或者第二个要删除的地址(不应该在我的测试用例中,列表框只有一个项目)引爆代码消失的核设备吗?

这是崩溃后的最后一个日志文件:

Date: 2/11/2015 12:45:34 PM
Message: Reached frmMain.UpdateGUIAfterTableSend

Date: 2/11/2015 12:45:34 PM
Message: Reached frmMain.UpdateGUIAfterTableSend#2

Date: 2/11/2015 12:45:34 PM
Message: Reached frmMain.UpdateGUIAfterTableSend#3

Date: 2/11/2015 12:45:34 PM
Message: From application-wide exception handler: System.InvalidOperationException: InvalidOperationException
   at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
   at HHS.frmMain.SendDeliveries()
   at HHS.frmMain.menuItemSEND_Deliveries_Click(Object sender, EventArgs e)
   at System.Windows.Forms.MenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.Menu.ProcessMnuProc(Control ctlThis, WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Form.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
   at System.Windows.Forms.Application.Run(Form fm)
   at HHS.Program.Main()

更新

我在&#34; RemoveAt&#34;之前添加了另一个日志消息。线,并得到我期待的:

Date: 2/11/2015 1:36:53 PM
Message: About to remove listbox value DSD_3_20150209151047000 at index 0

......但在此之后它会立即崩溃,所以Rake36必须是正确的。

更新2

我有点相信这会起作用:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0}", listboxVal));
        listBoxWork.Items.RemoveAt(listBoxWork.Items.IndexOf(listboxVal));
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}

......但是当我听到告诉人的声音时,我的希望破灭了!&#34; bong!&#34;然后在日志文件中搜索它:

Message: About to remove listbox value DSD_3_20150209151047000

Date: 2/11/2015 1:58:18 PM
Message: From frmMain.UpdateGUIAfterTableSend: Specified argument was out of the range of valid values.; Inner Ex: ; Stack Trace:    at System.Collections.ArrayList.RemoveAt(Int32 index)
   at System.Windows.Forms.ListBox.ObjectCollection.RemoveAt(Int32 index)
   at HHS.frmMain.UpdateGUIAfterTableSend(String listboxVal)
   at HHS.frmMain.SendDeliveries()
   at HHS.frmMain.menuItemSEND_Deliveries_Click(Object sender, EventArgs e)
   at System.Windows.Forms.MenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.Menu.ProcessMnuProc(Control ctlThis, WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Form.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
   at System.Windows.Forms.Application.Run(Form fm)
   at HHS.Program.Main()


Date: 2/11/2015 1:58:19 PM
Message: From application-wide exception handler: System.InvalidOperationException: InvalidOperationException
   at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
   at HHS.frmMain.SendDeliveries()
   at HHS.frmMain.menuItemSEND_Deliveries_Click(Object sender, EventArgs e)
   at System.Windows.Forms.MenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.Menu.ProcessMnuProc(Control ctlThis, WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Form.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)
   at System.Windows.Forms.Application.Run(Form fm)
   at HHS.Program.Main()

我甚至在&#34; RemoveAt&#34;之前添加了这个。行:

listBoxWork.DataSource = null;

......但没有任何区别。

更新3

这个(受到Ravi M Patel的启发)也崩溃了:

private void UpdateGUIAfterTableSend(String listboxVal)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
    try
    {
        BindingSource bs = new BindingSource();
        bs.DataSource = listBoxWork.DataSource;
        for (int i = bs.Count - 1; i >= 0; i--)
        {
            if (bs[i].ToString().Contains(listboxVal))
            {
                ExceptionLoggingService.Instance.WriteLog(String.Format("About to remove listbox value {0} at index {1}", listboxVal, i));
                bs.RemoveAt(i);
                ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend#3");
            }
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format("{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From frmMain.UpdateGUIAfterTableSend: {0}", msgInnerExAndStackTrace));
    }
}

...并且日志文件(相关)内容为:

Date: 2/11/2015 2:09:59 PM
Message: About to remove listbox value DSD_3_20150209151047000 at index 0

Date: 2/11/2015 2:09:59 PM
Message: Reached frmMain.UpdateGUIAfterTableSend#3

Date: 2/11/2015 2:09:59 PM
Message: From application-wide exception handler: System.InvalidOperationException: InvalidOperationException
   at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
   at HHS.frmMain.SendDeliveries()

更新4

有关实际修复的信息,请参阅How can I remove more than one item from a listbox?

4 个答案:

答案 0 :(得分:2)

异常来自与你所展示的逻辑完全不同的地方。它是触发的应用程序范围的异常处理程序,而不是将创建跟踪“From frmMain.UpdateGUIAfterTableSend”的异常处理程序。异常很可能是因为绑定具有副作用。看一下bindingsource和你附加的事件。 还要在删除后添加跟踪,以便您可以看到删除实际发生或使用调试器。您的更新3显示删除确实发生。

在SendDeliveries中添加更多痕迹。你的问题在那里

答案 1 :(得分:1)

在您重复迭代时,您无法销毁列表中的元素。您需要首先收集要删除的项目,然后迭代这些项目,从过程中的原始列表中删除。

有用的模式:

Items.RemoveAt(Items.IndexOf(itemToDelete))

答案 2 :(得分:1)

更新2

使用以下内容替换try {}块中的代码:

BindingSource bs = listBoxWork.DataSource as BindingSource;
List<string> values = bs.DataSource as List<string>;
values.RemoveAll(v => v.Contains(listboxVal));
bs.ResetBindings(false);

更新

@TaW是对的。我正在猜测这个问题。从数据源中删除元素时,它也会更新ListBox。由于您要删除选定的值,因此用户界面会尝试选择下一个值而爆炸,则没有。

所以要做到这一点,你应该这样做。假设您已将列表框绑定到名为values的arraylist。

现在,在您的方法中,从values而不是BindingSource中删除项目。在您的情况下,如果确定列表中只有一个值。您所要做的就是values.Remove("valuetoremove");然后重置绑定,如此。

ArrayList values = bs.DataSource as ArrayList;
values.Remove("valuetoremove");
bs.ResetBindings(false);

您可以忽略我的答案。我认为你是类型转换的数据源已经是BindingSource类型,所以你对原始语句很好。 BindingSource bs = listBoxWork.DataSource as BindingSource;我不知道,因为我们无法在此处查看完整的代码。


如果你想从绑定源中获得正确的行为。你最好用这种方式。

BindingSource bs = new BindingSource();
bs.DataSource = listBoxWork.DataSource;

尝试使用原始代码。

答案 3 :(得分:0)

如果你能做到这一点:

List<string> list = (List<string>) listBoxWork.DataSource;

你应该能够做到这一点:

    for (int i = 0; i < list.Count; i++)
    {
        if (list[i].Contains(listboxVal))
        {
            list.RemoveAt(i);
        }
    }

    listBox1.DataSource = null;
    listBox1.Items.Clear();
    listBox1.DataSource = list;

但我怀疑你的DataSource 实际上是List<string>,因为这根本行不通。 (只显示字符串的长度!)

所以你应该修改代码以适合实际 DataSource的类型!

如果是这样的话:

List<AString> list = new List<AString>();

这样的课程:

class AString
{
    public string s { get; set; }
    public AString(string s_) { s = s_; }
    public override string ToString()  {   return s;    }
}

然后这会起作用:

    for (int i = list.Count - 1; i > 0;  i--)
    {
        if (list[i].s.Contains(listboxVal))
        {
            list.RemoveAt(i); 
        }
    }       
    listBox1.DataSource = null;
    listBox1.Items.Clear();
    listBox1.DataSource = list;

注意ListBox被修改后的DataSource doesn't automatically update

您可以使用BindingList

,而不是进行相当不满意的往返
    BindingList<AString> data = new BindingList<AString>();
    foreach (AStrings in list) data.Add(s);
    listBox1.DataSource = data;

    for (int i = data.Count - 1; i > 0; i--)
    {
        if (data[i].s.Contains("8"))
        {
            data.RemoveAt(i); // (list[i]);
        }
    }

现在立即反映清除..