C# 调用 ListView 不起作用(后台线程)
Posted
技术标签:
【中文标题】C# 调用 ListView 不起作用(后台线程)【英文标题】:C# Invoking ListView not working (Background Thread) 【发布时间】:2020-03-10 02:56:21 【问题描述】:我正在使用 WinForms。 我想从后台线程填充 ListView,但是当我调用 listview 时,我的程序停止并显示错误。错误是“无法访问已处置的对象。对象名称是:ListView。”而当我把这个方法
lvValidate.Invoke((Action)delegate
lvValidate.Items.Add(listitem);
);
在 try-catch 块中,我的程序开始滞后。我不知道问题出在哪里,但我的 Invoke 方法是:
static class Intercept
internal static void Invoke(this Control control, Action action)
control.Invoke(action);
该错误仅在我关闭表单并打开另一个表单(在同一程序中)时显示。在包含 ListView 的表单中,数据不可读,并且似乎加载了数千次。
这就是我的 DoWork,ProgressChanged,RunWorkerCompleted 事件的作用。
private void bgwLoad_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
string commandText = "SELECT * FROM works";
mysqlCommand command = new MySqlCommand(commandText, connection);
MySqlDataAdapter da = new MySqlDataAdapter(command);
connection.Close();
connection.Open();
reader = command.ExecuteReader();
connection.Close();
DataTable dt = new DataTable();
da.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
DataRow dr = dt.Rows[i];
ListViewItem listitem = new ListViewItem(dr["ID"].ToString(), dr["Date"].ToString());
listitem.SubItems.Add(dr["Date"].ToString());
listitem.SubItems.Add(dr["Name"].ToString());
listitem.SubItems.Add(dr["WorkNumber"].ToString());
listitem.SubItems.Add(dr["WorkCode"].ToString());
listitem.SubItems.Add(dr["CoreThread"].ToString());
listitem.SubItems.Add(dr["Tech"].ToString());
listitem.SubItems.Add(dr["From"].ToString());
listitem.SubItems.Add(dr["To"].ToString());
listitem.SubItems.Add(dr["Validate"].ToString());
listitem.SubItems.Add(dr["Validate2"].ToString());
lvValidate.Items.Add(listitem);
private void bgwLoad_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
private void bgwLoad_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
picLoading.Visible = false;
【问题讨论】:
显然你已经废弃了 ListView。或者可能只是里面的东西? 不清楚您的显示技术(以及 ListView)是什么:WinForms? WPF? ASP.NET?其他? 一般我在后台使用report progress的方式,将数据发送到ListView所在的主线程。然后在报告进度方法中将数据发送到列表视图。 @Christopher WinForms 是技术 @Christopher 我不知道我处理了什么。代码中没有手动处理任何内容。 【参考方案1】:尝试以下方法:
Dispatcher.CurrentDispatcher.Invoke(() =>
lvValidate.Items.Add(listitem);
);
编辑:
或者试试这个:
public static void AddItem(ListItem listitem)
if (lvValidate.InvokeRequired)
AddItemDelegate d = new AddItemDelegate (AddItem);
lvValidate.Invoke(d, new object[] listitem );
else
lvValidate.Invoke(new Action(() =>
lvValidate.Items.Add(listitem);
));
delegate void AddItemDelegate(ListItem listitem);
然后调用:
AddItem(listitem);
【讨论】:
不起作用。我在 DoWork 活动中使用过。它显示跨线程错误消息:跨线程操作无效:控件'lvValidate'从创建它的线程以外的线程访问。 和以前一样的错误。后台工作人员正在工作,但是当我关闭表单时,程序崩溃并显示问题“无法访问已处置的对象。对象名称为:ListView。” 然后你必须将数据存储在你的调用上下文中,从你打开表单并在每次打开表单时传递它。 你应该在 cunstructor 中设置 ItemSource 我不知道在哪里做这个【参考方案2】:根据您的 cmets,您正在尝试使用 DataAdapter 从数据库中检索数据,然后将数据零碎地分发。这不起作用有几个原因:
数据适配器
DataAdapter 类的共同点是它们只工作,而 DBConnection 是主动打开的。这就是为什么您会得到“无法访问已处置的对象”的原因。因为当您尝试使用它时,它的连接已经被释放。处置不是你应该拖延的事情。或者根本分手。在 DoWork() 中保持使用(希望得到的)正确的位置。
因此,您始终必须将 DataAdapter 的数据复制到非适配器集合中。真的任何旧清单都可以。这将暂时使内存负载加倍,并且可能会产生一些东西供 GC 清理,但实际上它们是唯一的建议方式。
仅在已完成时批量写入
虽然理论上您可以通过进度报告分发部分读取/处理结果,但不建议尝试。编写 GUI 需要大量开销。我第一次也是唯一一次这样做,我最终用写入和绘制操作锁定了我的 GUI 线程。看起来我从来没有做过多任务处理。更新进度条的成本几乎很低,不会真正引起问题。
默认模式是仅在获得所有数据后才覆盖相关数量的数据。
如果您遇到异常或取消,该模式是假定所有数据都是错误的,并且在 UI 上没有。
我喜欢将 BackgroundWorker 称为“多任务/线程训练轮”。他们教你所有这些东西。第一部分,通过分发 akward。第二部分,如果您尝试在无效情况下使用 Result,则实际抛出异常。
你可能收回了太多
也许数据库最常见的错误是尝试检索大量数据,然后在客户端中进行处理或过滤。一个常见的错误,所以避免它。
对于您定义为 1 页的任何数据,用户可以处理多少数据是有限制的。我的建议是一次不要超过 100 个数据字段。如果您需要过滤、分页或排序,总是在查询中进行。将这些东西移动到客户端会通过网络移动大量不必要的数据,然后处理/过滤的速度就会比数据库慢。
示例代码
这实际上是我的第一个 BGW 项目。多年来我对其进行了一些更新,但大部分内容仍然有效:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
if (!bgwPrim.IsBusy)
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
private void btnPrimCancel_Click(object sender, EventArgs e)
if (bgwPrim.IsBusy)
bgwPrim.CancelAsync();
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
if (PrimeCandidate % j == 0)
isNoPrime = true;
if (!isNoPrime)
Primes.Add(PrimeCandidate);
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
pgbPrim.Value = e.ProgressPercentage;
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
sbOutput.Append(Prim.ToString() + Environment.NewLine);
tbPrim.Text = sbOutput.ToString();
else
tbPrim.Text = "Operation canceled by user or Exception";
#endregion
【讨论】:
嘿,我只使用 DataAdapter 来处理查询。 DataAdapter 执行,数据将流入 DataTable,这是本地的事情。从 DataTable 我想把数据放到 UI ListView 中。我不知道为什么这么难我只想将数据从 DoWork 处理到 UI 线程。我在这个项目上工作了一天多,我找不到解决方案。你的很好,但仍然不适合我 @LarryPetshow 将 DataTable 分配给 DoWork 事件的 REsult 属性。然后从 RunworkerCompleted 事件的结果中检索它。从那里分配它。 BGW 不遗余力地在主线程上引发 ReportProgress 和 RunWorkerCompleted 事件,因此不需要从这两个事件中调用。 如果我这样做,输出(结果)将是 System.Data.DataRow。而我的 ListView 则充满了这个词。但最重要的是,我编辑并复制了 DoWork 代码。 @LarryPetshow 自从我使用 DataAdapter 已经很久了。所以除了需要翻译的基本部分之外,我不记得太多了。以上是关于C# 调用 ListView 不起作用(后台线程)的主要内容,如果未能解决你的问题,请参考以下文章
完整功能中的ajax调用后刷新jquery mobile listview不起作用