I've got this code in a Windows CE app:
for (int i = listBoxWork.Items.Count - 1; i > -1; i--)
{
if (listBoxWork.Items[i].ToString().IndexOf(listboxVal) != -1)
{
listBoxWork.Items.RemoveAt(i);
}
}
It crashes with "Invalid Operation Exception at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()..."
I try the same exact code in a "regular" Windows forms app, and it works perfectly. If this is something Windows CE can't handle in this way, how can I remove an item from a Listbox?
UPDATE
This is the "VS 2013 code" (Target framework == .NET Framework 4.5.1, Platform target == Any CPU) that works (the "blaINV" item in the listbox is removed):
private void button44_Click(object sender, EventArgs e)
{
String listboxVal = "blaINV";
for (int i = listboxWork.Items.Count - 1; i >= 0; --i)
{
if (listboxWork.Items[i].ToString().IndexOf(listboxVal) != -1)
{
listboxWork.Items.RemoveAt(i);
}
}
}
The using statements on that form (this is a "sandbox" form where I test out all kinds of things):
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.DirectoryServices.AccountManagement;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
The "VS 2008" code follows.
Note first, though, that this project's Target framework should be .NET 3.5, but "Target Framework:" is grayed out in Project > Properties > Application tab; if I select Project > Change Target Platform, "Current platform:"== Windows CE)
But if I go to Project > Properties > Build tab, Configuration is set to "Active (Debug)", Platform is set to "Active (Any CPU)", and Platform target is set to "Any CPU" and is the only option in the dropdown.
On the Devices tab (Project > Properties > Devices), Target device is set to "Pocket PC 2003 SE Emulator" but I don't really use that. When I need to test, I just copy the .exe to the handheld device.
Anyway, here's the code (which fails):
private void UpdateGUIAfterTableSend(String listboxVal)
{
for (int i = listBoxWork.Items.Count - 1; i >= 0; --i)
{
if (listBoxWork.Items[i].ToString().IndexOf(listboxVal) != -1)
{
listBoxWork.Items.RemoveAt(i);
}
}
}
As you can see, it is identical except that a "bogus" string to search for is provided in the working code. The actual listbox contains a value that matches "listboxVal"; yet, not only is it not removed, the exception as noted above occurs.
Here are the only using statements on the non-working form:
using System;
using System.Windows.Forms;
namespace HHS
{
using System.Collections.Generic; // R# put this "using" here; like Don Henley in "The Last Resort," I don't know why
UPDATE 2
I moved the after-the-namespace using to above-the-namespace just to see if it would, by any chance, make any difference. It didn't.
UPDATE 3
I added a catch block to the code:
try
{
for (int i = listBoxWork.Items.Count - 1; i >= 0; --i)
{
if (listBoxWork.Items[i].ToString().IndexOf(listboxVal) != -1)
{
listBoxWork.Items.RemoveAt(i);
}
}
}
catch (Exception ex)
{
MessageBox.Show(String.Format(
"Exception in UpdateGUIAfterTableSend(). Message == {0}; InEx == {1}; StackTrace == {2}",
ex.Message, ex.InnerException, ex.StackTrace));
}
...but I don't see the Message Box - the previous exception displays and, on its dismissal, the app crashes - even with a thin metallic "bing" sound, which I've never heard from the device before.
UPDATE 4
Okay, to try to get to the bottom of this, I sprinkled MessageBox.Show()s galore into the code:
private void UpdateGUIAfterTableSend(String listboxVal)
{
try
{
MessageBox.Show("Made it before for loop in UpdateGUIAfterTableSend()");
for (int i = listBoxWork.Items.Count - 1; i >= 0; --i)
{
MessageBox.Show("Made it before if condition in UpdateGUIAfterTableSend()");
if (listBoxWork.Items[i].ToString().IndexOf(listboxVal) != -1)
{
MessageBox.Show("Made it before remove line in UpdateGUIAfterTableSend()");
listBoxWork.Items.RemoveAt(i);
}
}
MessageBox.Show("Made it before listBoxMessages.Items.Add() in
UpdateGUIAfterTableSend()");
listBoxMessages.Items.Add(String.Format("{0} sent at {1}", listboxVal,
DateTime.Now.ToLongTimeString()));
MessageBox.Show("Made it after listBoxMessages.Items.Add() in
UpdateGUIAfterTableSend()");
}
catch (Exception ex)
{
MessageBox.Show(String.Format(
"Exception in UpdateGUIAfterTableSend(). Message == {0}; InEx == {1}; StackTrace
== {2}", ex.Message, ex.InnerException, ex.StackTrace));
}
}
This is called like so:
private void menuItemSEND_Inventories_Click(object sender, EventArgs e)
{
SendInventories();
}
private void SendInventories()
{
Cursor curse = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try
{
foreach (String tblname in listBoxWork.Items)
{
// Ignore DSD tables
if (tblname.IndexOf("DSD") == 0) continue;
int siteNum = HHSDBUtils.GetSiteNumForTableName(tblname);
String fileName = HHSUtils.GetGeneratedINVFileName(siteNum);
String xmlData = HHSDBUtils.SaveAndGetINVDataAsXMLFromTable(tblname, fileName);
String uri =
String.Format("http:100.200.400.800:1500/api/inventory/sendXML/duckbill/platypus/{0}", fileName);
RESTUtils.SendHTTPRequestNoCredentials(uri, RESTUtils.HttpMethods.POST, xmlData,
"application/xml");
HHSDBUtils.DeleteTableReference(tblname, "INV");
HHSDBUtils.DropSQLiteTable(tblname, siteNum);
UpdateGUIAfterTableSend(tblname);
}
}
finally
{
Cursor.Current = curse;
}
}
So you can see, nothing happens after UpdateGUIAfterTableSend() is called. This is what I see before selecting Send > Inventories and setting off this chain of events:
...and this is what I see just before the crash (the listboxitem removal code actually is working, I can see now):
On dismissing that last "made it" MessageBox.Show(), it crashes as noted previously. Why?!?!? There's nothing else going on after that!?!
UPDATE 5
So I added a catch block to the method ( SendInventories()) that is actually seemingly the culprit:
catch (Exception ex)
{
MessageBox.Show(String.Format(
"Exception in SendInventories(); Message == {0}, InEx == {1}, StackTrace == {2}",
ex.Message, ex.InnerException, ex.StackTrace));
}
...and I see this:
However, by catching the exception in this way, the app doesn't crash. And it seems harmless - what I want to accomplish has been accomplished. So, although certainly curious about the ins and outs of this exception (no pun intended), maybe I'll just suppress/swallow it for now...
UPDATE 6
So if I change my catch block to this:
catch (Exception ex)
{
if (!ex.Message.Contains("InvalidOperationException"))
{
MessageBox.Show(String.Format(
"Exception in SendInventories(); Message == {0}, InEx == {1}, StackTrace == {2}",
ex.Message, ex.InnerException, ex.StackTrace));
}
}
...it works just fine (I see no exception, and both listboxes are updated as intended). I guess I'll just chalk it up to the vagaries of the wacky Windows CE world.