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

时间:2012-02-08 13:43:43

标签: c# .net winforms multithreading

我试图从另一个类填充列表视图,但我发现了这个错误: "跨线程操作无效:Control' listView1'从创建它的线程以外的线程访问。 "

在我的课堂上,我宣布我的列表视图如下:

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();
        }

有什么问题?

5 个答案:

答案 0 :(得分:6)

通常我这样做:

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"));
    }   
    //...
    }
}

答案 1 :(得分:2)

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

为此,必须使用ListViewthis.Invoke(在WPF中)完成与更新this.Dispatcher.Invoke相关的所有内容。

修改
例如this thread here

示例代码:

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
    }
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

int *numbersProcessFour(int *x, int size) {
    int i;
    for(i = 0; i + 3 < size; i += 3){
        reverse_array(x + i, 3);
    }
    if(size - i > 1)
        reverse_array(x + i, size - i);
    return x;
}

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

答案 4 :(得分:0)

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

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

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

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