C# 从另一个线程填充listview
我试图从另一个类填充listview,但出现以下错误: “跨线程操作无效:从创建控件“listView1”的线程以外的线程访问该控件。” 在我的类中,我声明我的listview如下:C# 从另一个线程填充listview,c#,.net,winforms,multithreading,C#,.net,Winforms,Multithreading,我试图从另一个类填充listview,但出现以下错误: “跨线程操作无效:从创建控件“listView1”的线程以外的线程访问该控件。” 在我的类中,我声明我的listview如下: class CheckBlankPages { public String[] pdfFiles { get; set; } ListView _ListVireRef; public int NrCRT = 1; public CheckBlankPages(Str
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);
}
}
}
using System;
using System.Windows.Forms;
namespace TestWinFormsThreding
{
class TestFormControlHelper
{
delegate void UniversalVoidDelegate();
/// <summary>
/// Call form control action from different thread
/// </summary>
public static void ControlInvoke(Control control, Action function)
{
if (control.IsDisposed || control.Disposing)
return;
if (control.InvokeRequired)
{
control.Invoke(new UniversalVoidDelegate(() => ControlInvoke(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.ControlInvoke(listView1, () => listView1.Items.Add("Test"));
}
//...
}
}
在我的main表单中,我使用以下代码:
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();
}
有什么问题吗?在这里进行简单的搜索,会得到许多结果,告诉您不允许从创建控件的线程以外的线程更改GUI控件(跨线程GUI访问) 为此,必须使用
this.Invoke
或this.Dispatcher.Invoke
(在WPF中)完成与更新列表视图相关的所有操作
编辑
比如说
示例代码:
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线程。您需要在要更新的控件上使用。您可以检查该控件上的invokererequired
属性,查看是否需要使用Invoke
来更新控件通常我是这样做的:
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);
}
}
}
using System;
using System.Windows.Forms;
namespace TestWinFormsThreding
{
class TestFormControlHelper
{
delegate void UniversalVoidDelegate();
/// <summary>
/// Call form control action from different thread
/// </summary>
public static void ControlInvoke(Control control, Action function)
{
if (control.IsDisposed || control.Disposing)
return;
if (control.InvokeRequired)
{
control.Invoke(new UniversalVoidDelegate(() => ControlInvoke(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.ControlInvoke(listView1, () => listView1.Items.Add("Test"));
}
//...
}
}
使用系统;
使用System.Windows.Forms;
命名空间TestWinFormsThresding
{
类TestFormControlHelper
{
delegate void UniversalVoidDelegate();
///
///从不同线程调用窗体控件操作
///
公共静态void ControlInvoke(控件、操作函数)
{
if(control.IsDisposed | | control.Disposing)
返回;
if(control.invokererequired)
{
调用(新的UniversalVoidDelegate(()=>ControlInvoke(控件,函数));
返回;
}
函数();
}
}
公共部分类TestMainForm:Form
{
// ...
//这将从与MainForm线程不同的线程调用
私有void TestFunction()
{
TestFormCotrolHelper.ControlInvoke(listView1,()=>listView1.Items.Add(“测试”);
}
//...
}
}
当从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<string, ListViewItem>(item => new ListViewItem(item))).ToArray();
listView1.Items.AddRange(range);
}
}
}
命名空间窗口窗体应用程序1
{
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
}
无效附加项(字符串[]项)
{
如果(需要调用)
{
调用((MethodInvoker)委托{this.AddItems(items);});
返回;
}
ListViewItem[]范围=(items.Select(item=>newListViewItem(item))).ToArray();
listView1.Items.AddRange(范围);
}
}
}
函数第一次进入另一个线程时,调用invoke,函数只需再次调用自己,这次是在正确的线程上下文中。
实际工作在if()块之后只写一次。这是一种合理的做法,因为在应用程序中,您通常希望在不中断代码的情况下继续执行ListView更新等
我已经在用户控件之间完成了消息系统,其中包含了我想在后台更新的控件,它可能会变得非常混乱,因为你最终不得不在消息/事件中进行更多的填充/更新,混乱的代码是错误代码,所以我尝试了其他方法
有一个很好的方法,ListView填充/更新的缓慢部分通常是在ListViewItems的创建中,您可以在自己的线程中完全准备这些内容
现在,对于这类应用程序(使用Fill或update ListView,我不需要等待它准备就绪,然后代码才能继续),我的独立线程创建/准备ListViewItems,然后在线程完成后将准备好的项目添加到ListView非常快,因此,最终的ListView更新可以在用户几乎看不到的用户事件上完成。再加上“只添加你能看到的”,这真的是瞬间的。有几个额外的行,所以当滚动开始时,您可以添加更多的行。(你可能已经注意到youtube/facebook/windows图片浏览器都是这样做的)。因为我们已经准备好了ListViewItems,所以将它们添加到列表中非常简单。我的应用程序是windows窗体,如何使用Invoke方法填充ListView,就像您在windows窗体上使用ISynchronizeInvoke和Dispatcher中的Invoke方法在控件中修改ListView一样。的可能重复项