从另一个线程填充列表视图

我正在尝试从另一个类填充列表视图,但我发现这个错误:“跨线程操作无效:控制’listView1’从其创建的线程以外的线程访问。”

在我的class级中,我声明我的列表视图如下:

class CheckBlankPages { public String[] pdfFiles { get; set; } ListView _ListVireRef; public int NrCRT = 1; public CheckBlankPages(String[] pdfFiles = null, ListView listView = null) { this.pdfFiles = pdfFiles; _ListVireRef = listView; } public void StartCheckingPDF() { foreach (string pdf in pdfFiles) { String[] itm = { (NrCRT++).ToString(), pdf }; ListViewItem item = new ListViewItem(itm); _ListVireRef.Items.Add(item); } } } 

在我的MainForm中我使用此代码:

 DialogResult rezultat = openFileDialog1.ShowDialog(); if (rezultat == DialogResult.OK) { CheckBlankPages ck = new CheckBlankPages(openFileDialog1.FileNames, listView1); Thread CheckPDFs = new Thread(new ThreadStart(ck.StartCheckingPDF)); CheckPDFs.Start(); } 

怎么了?

通常我这样做:

 using System; using System.Windows.Forms; namespace TestWinFormsThreding { class TestFormCotrolHelper { delegate void UniversalVoidDelegate(); ///  /// Call form controll action from different thread ///  public static void ControlInvike(Control control, Action function) { if (control.IsDisposed || control.Disposing) return; if (control.InvokeRequired) { control.Invoke(new UniversalVoidDelegate(() => ControlInvike(control, function))); return; } function(); } } public partial class TestMainForm : Form { // ... // This will be called from thread not the same as MainForm thread private void TestFunction() { TestFormCotrolHelper.ControlInvike(listView1, () => listView1.Items.Add("Test")); } //... } } 

在SO上进行简单搜索会产生许多结果,告诉您不允许从创建控件的线程(跨线程GUI访问)以外的线程更改GUI控件。

为此,必须使用this.Invokethis.Dispatcher.Invoke (在WPF中)完成与更新ListView相关的所有操作。

编辑
比如这个post在这里 。

示例代码:

 private delegate void MyDelegate(string s); public void UpdateControl(Control targetControl, string text) { if (targetControl.InvokeRequired) { // THIS IS STILL THE IN THE CONTEXT OF THE THREAD MyDelegate call = new MyDelegate(UpdateControl); targetControl.Invoke(call, new object[] { text }); } else { // do control stuff // THIS IS IN THE CONTEXT OF THE UI THREAD } } 

您正在尝试从后台线程更新GUI线程。 您需要在要更新的控件上使用Invoke 。 您可以检查该InvokeRequired上的InvokeRequired属性,以查看是否需要使用Invoke来更新控件

从UI线程和其他线程调用函数时,避免重复代码或故障的巧妙技巧是:

 namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } void AddItems( string[] items ) { if(InvokeRequired) { Invoke((MethodInvoker) delegate { this.AddItems(items); }); return; } ListViewItem[] range = (items.Select(item => new ListViewItem(item))).ToArray(); listView1.Items.AddRange(range); } } } 

函数第一次进入另一个线程时,调用invoke并且函数只是再次调用自身,这次是在正确的线程上下文中。 然后在if()块之后仅将实际工作写下一次。

这是一个合理的事情,因为通常在应用程序中,您希望ListView更新等继续运行而不会占用您的代码。

我已经完成了包含我想要在后台更新的控件的用户控件之间的消息系统,它可能会变得非常混乱,因为你最终不得不消息/事件不仅仅是填充/更新,凌乱的代码是有缺陷的代码,所以我尝试了其他方法。

有一个很好的方式,ListView填充/更新的缓慢部分通常是在ListViewItems的创建,你可以在自己的线程中完全准备。

所以现在,对于这种类型的应用程序(使用Fill或更新ListView,在我的代码可以继续之前我不需要等待它准备就绪),我的单独线程创建/准备ListViewItems然后将准备好的项目添加到ListView时线程完成非常快,因此最终的ListView更新可以在用户几乎无法察觉的用户事件上完成。 添加到这个’只添加你能看到的那些’,它真的是瞬间的。 有几个额外的行,所以当滚动开始时,你可以添加更多。 (您可能已经注意到youtube / facebook / windows图片浏览器都是这样做的)。 因为在我们的例子中我们已经准备好了ListViewItems,所以将它们添加到列表中非常简单。