为什么此数组列表删除代码失败?
Why is this array list removal code failing?
我试图通过传递相应的文本、在列表框中查找它并删除该项目来从列表框中删除特定值。使用此代码:
private void UpdateGUIAfterTableSend(String listboxVal)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
try
{
listBoxWork.DataSource = null; // <= It seems necessary to set this to null to circumvent "Value does not fall within the expected range"
for (int i = listBoxWork.Items.Count - 1; i >= 0; --i)
{
if (listBoxWork.Items[i].ToString().Contains(listboxVal))
{
listBoxWork.Items.RemoveAt(i);
}
}
}
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/10/2015 1:48:07 PM
Message: Reached frmMain.UpdateGUIAfterTableSend
Date: 2/10/2015 1:48:07 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()
请注意,记录异常的是 "application-wide exception handler",而不是 UpdateGUIAfterTableSend()
中的 catch 块
通过像这样设置其数据源来填充列表框:
listBoxWork.DataSource = workTables;
workTables 是由查询填充的字符串列表:
List<String> workTables = hhsdbutils.GetWorkTableNames();
我对导致问题的原因(及其解决方案)以及为什么 UpdateGUIAfterTableSend() 中的 catch 块没有记录抛出的异常感到困惑。
更新
在史蒂夫发表评论后,我取消了将数据源设置为空的评论。那么例外是:
Date: 2/10/2015 2:19:58 PM
Message: From frmMain.UpdateGUIAfterTableSend: Value does not fall within the expected range.; Inner Ex: ; Stack Trace: at System.Windows.Forms.ListBox._VerifyNoDataSource()
at System.Windows.Forms.ListBox.ObjectCollection.RemoveAt(Int32 index)
at HHS.frmMain.UpdateGUIAfterTableSend(String listboxVal)
更新 2
阿迪莱斯特:
private void SendDeliveries()
{
ExceptionLoggingService .Instance.WriteLog("Reached frmMain.SendDeliveries");
Cursor curse = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try
{
bool firstRecord = false;
bool lastRecord = false;
foreach (String tblname in listBoxWork.Items)
{
// Ignore INV tables
if (tblname.IndexOf("INV") == 0) continue;
String tblSiteNum = hhsdbutils.GetSiteNumForTableName(tblname);
String fileName = HHSUtils.GetGeneratedDSDFileName(tblSiteNum);
String xmlData = hhsdbutils.GetDSDDataAsXMLFromTable(tblname, fileName);
String uri = String.Format("{0}delivery/sendXML/duckbill/platypus/{1}", HHSConsts.BASE_REST_URL, fileName);
fileXferImp = HHSConsts.GetFileTransferMethodology();
fileXferImp.SendDataContentsAsXML(uri, xmlData, tblname, siteNum, firstRecord, lastRecord);
String tableRefVal = HHSUtils.GetTableRefValForTableName(tblname, "DSD");
hhsdbutils.DeleteTableReference(tableRefVal, "DSD");
hhsdbutils.DropTable(tblname, tblSiteNum); // <- Will actually do this only after creating replacement tables in code
UpdateGUIAfterTableSend(tblname);
}
}
finally
{
Cursor.Current = curse;
}
}
你的两种做法都是错误的。我的意思是将 DataSource
设置为空,并直接从 ListBox
中删除项目。
当 ListBox 是数据绑定时(即 DataSource
集),您不能直接修改 Items
集合。您必须需要删除基础数据源中的项目。数据源应通知数据绑定引擎有关项目的删除,以便 ListBox 项目同步。
为此,您的 DataSource
应该支持更改通知。如果是 WPF,请使用 ObservableCollection
。如果是winforms,使用BindingList<T>
.
因此您的代码应如下所示:
for (int i = yourDataSource.Count - 1; i >= 0; --i)
{
if (yourDataSource[i].ToString().Contains(listboxVal))
{
yourDataSource.RemoveAt(i);
}
}
虽然您没有提供引发异常的实际代码,但很明显您正在从正在枚举的列表中删除一个项目(可能通过 foreach)。如果集合发生更改,标准集合会使任何枚举器无效。在 for 循环中删除不是问题,这就是为什么您没有在那里捕获异常的原因。似乎在 foreach 循环内的另一个方法中调用了此方法。
当设置 ListBox 的 DataSource 时使用 BindingSource 而不是直接使用 List(Of String)
这是必需的,否则绑定引擎将看不到 DataSource 中的任何更改。
假设你想用这样的东西设置你的列表框项目,并想删除包含字符串 "Test":
的每个项目
listBoxWork = new ListBox();
List<string> elements = new List<string>()
{
"Test1", "Test2", "Test3", "Example1", "Example2"
};
BindingSource bs = new BindingSource();
bs.DataSource = elements;
listBoxWork.DataSource = bs;
现在在您尝试删除项目的代码中使用
private void UpdateGUIAfterTableSend(String listboxVal)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
try
{
BindingSource bs = listBoxWork.DataSource as BindingSource;
for (int i = bs.Count - 1; i >= 0; --i)
{
if (bs[i].ToString().Contains("Test"))
{
bs.RemoveAt(i);
}
}
}
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));
}
}
我试图通过传递相应的文本、在列表框中查找它并删除该项目来从列表框中删除特定值。使用此代码:
private void UpdateGUIAfterTableSend(String listboxVal)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
try
{
listBoxWork.DataSource = null; // <= It seems necessary to set this to null to circumvent "Value does not fall within the expected range"
for (int i = listBoxWork.Items.Count - 1; i >= 0; --i)
{
if (listBoxWork.Items[i].ToString().Contains(listboxVal))
{
listBoxWork.Items.RemoveAt(i);
}
}
}
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/10/2015 1:48:07 PM
Message: Reached frmMain.UpdateGUIAfterTableSend
Date: 2/10/2015 1:48:07 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()
请注意,记录异常的是 "application-wide exception handler",而不是 UpdateGUIAfterTableSend()
中的 catch 块通过像这样设置其数据源来填充列表框:
listBoxWork.DataSource = workTables;
workTables 是由查询填充的字符串列表:
List<String> workTables = hhsdbutils.GetWorkTableNames();
我对导致问题的原因(及其解决方案)以及为什么 UpdateGUIAfterTableSend() 中的 catch 块没有记录抛出的异常感到困惑。
更新
在史蒂夫发表评论后,我取消了将数据源设置为空的评论。那么例外是:
Date: 2/10/2015 2:19:58 PM
Message: From frmMain.UpdateGUIAfterTableSend: Value does not fall within the expected range.; Inner Ex: ; Stack Trace: at System.Windows.Forms.ListBox._VerifyNoDataSource()
at System.Windows.Forms.ListBox.ObjectCollection.RemoveAt(Int32 index)
at HHS.frmMain.UpdateGUIAfterTableSend(String listboxVal)
更新 2
阿迪莱斯特:
private void SendDeliveries()
{
ExceptionLoggingService .Instance.WriteLog("Reached frmMain.SendDeliveries");
Cursor curse = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try
{
bool firstRecord = false;
bool lastRecord = false;
foreach (String tblname in listBoxWork.Items)
{
// Ignore INV tables
if (tblname.IndexOf("INV") == 0) continue;
String tblSiteNum = hhsdbutils.GetSiteNumForTableName(tblname);
String fileName = HHSUtils.GetGeneratedDSDFileName(tblSiteNum);
String xmlData = hhsdbutils.GetDSDDataAsXMLFromTable(tblname, fileName);
String uri = String.Format("{0}delivery/sendXML/duckbill/platypus/{1}", HHSConsts.BASE_REST_URL, fileName);
fileXferImp = HHSConsts.GetFileTransferMethodology();
fileXferImp.SendDataContentsAsXML(uri, xmlData, tblname, siteNum, firstRecord, lastRecord);
String tableRefVal = HHSUtils.GetTableRefValForTableName(tblname, "DSD");
hhsdbutils.DeleteTableReference(tableRefVal, "DSD");
hhsdbutils.DropTable(tblname, tblSiteNum); // <- Will actually do this only after creating replacement tables in code
UpdateGUIAfterTableSend(tblname);
}
}
finally
{
Cursor.Current = curse;
}
}
你的两种做法都是错误的。我的意思是将 DataSource
设置为空,并直接从 ListBox
中删除项目。
当 ListBox 是数据绑定时(即 DataSource
集),您不能直接修改 Items
集合。您必须需要删除基础数据源中的项目。数据源应通知数据绑定引擎有关项目的删除,以便 ListBox 项目同步。
为此,您的 DataSource
应该支持更改通知。如果是 WPF,请使用 ObservableCollection
。如果是winforms,使用BindingList<T>
.
因此您的代码应如下所示:
for (int i = yourDataSource.Count - 1; i >= 0; --i)
{
if (yourDataSource[i].ToString().Contains(listboxVal))
{
yourDataSource.RemoveAt(i);
}
}
虽然您没有提供引发异常的实际代码,但很明显您正在从正在枚举的列表中删除一个项目(可能通过 foreach)。如果集合发生更改,标准集合会使任何枚举器无效。在 for 循环中删除不是问题,这就是为什么您没有在那里捕获异常的原因。似乎在 foreach 循环内的另一个方法中调用了此方法。
当设置 ListBox 的 DataSource 时使用 BindingSource 而不是直接使用 List(Of String)
这是必需的,否则绑定引擎将看不到 DataSource 中的任何更改。
假设你想用这样的东西设置你的列表框项目,并想删除包含字符串 "Test":
的每个项目listBoxWork = new ListBox();
List<string> elements = new List<string>()
{
"Test1", "Test2", "Test3", "Example1", "Example2"
};
BindingSource bs = new BindingSource();
bs.DataSource = elements;
listBoxWork.DataSource = bs;
现在在您尝试删除项目的代码中使用
private void UpdateGUIAfterTableSend(String listboxVal)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmMain.UpdateGUIAfterTableSend");
try
{
BindingSource bs = listBoxWork.DataSource as BindingSource;
for (int i = bs.Count - 1; i >= 0; --i)
{
if (bs[i].ToString().Contains("Test"))
{
bs.RemoveAt(i);
}
}
}
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));
}
}