使用backgroundWorker时加载ListView和ImageList

时间:2015-01-17 20:36:48

标签: c# listview backgroundworker imagelist

加载图像列表和列表视图显然会导致UI挂起一点。因此,我想加载用户界面并让用户知道我们正在处理事情并让后台工作人员做这件事。我尝试使用formLoad来调用" backgroundWorker1.RunWorkerAsync();"

但是我得到了#34;跨线程操作无效:控制' listView1'从其创建的线程以外的线程访问。"

我戳了一下,发现我需要稍微更改一下代码。所以这就是我所拥有的:

    private void WatermarkPicker_Load(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }


private void GetImages()
    {
        DirectoryInfo dir = new DirectoryInfo(@"c:\pics");
        this.listView1.View = View.LargeIcon;
        this.imageList1.ImageSize = new Size(100, 100);
        if (listView1.InvokeRequired)
        {
            listView1.Invoke(new MethodInvoker(
                () => this.listView1.LargeImageList = this.imageList1));
        }
        else
        {
            this.listView1.LargeImageList = this.imageList1;
        }
        int j = 0;
        foreach (FileInfo file in dir.GetFiles())
        {
            try
            {
                //this.imageList1.Images.Add(Image.FromFile(file.FullName));
                imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));
                ListViewItem item = new ListViewItem(file.Name);
                item.Tag = file.Name;
                item.ImageIndex = j;
                this.listView1.Items.Add(item);
                j++;
            }
            catch
            {
                Console.WriteLine("This is not an image file");
            }
        }
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        GetCurrentLogos();
        GetImages();
    }

满足if(listView1.InvokeRequired),并运行listView1.invoke。但是UI中的listView中没有显示任何内容。虽然没有错误。

3 个答案:

答案 0 :(得分:2)

您应该移动尝试访问Doview事件之外的Listview和ImageList的所有代码,并使用DoWork代码中引发的ProgressChanged事件。

这样,ProgressChanged事件中的代码在正确的UI线程中执行(假设在UI线程中创建了BackgroundWorker)。

例如:

private void WatermarkPicker_Load(object sender, EventArgs e)
{
    listView1.View = View.LargeIcon;
    listView1.LargeImageList = imageList1;

    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
    backgroundWorker1.DoWork += backgroundWorker1_DoWork;
    backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    // Do not try to use in any way an object derived from Control
    // like the ListView when you are inside the DoWork method.....
    BackgroundWorker worker = sender as BackgroundWorker;
    DirectoryInfo dir = new DirectoryInfo(@"c:\pics");
    foreach (FileInfo file in dir.GetFiles())
    {
        // You can't use imageList here
        // imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));

        // Raise the ProgressChanged event. 
        // The code there will execute in the UI Thread
        worker.ReportProgress(1, file);        
    }
}

private void backgroundWorker1_ProgressChanged(object sender,  ProgressChangedEventArgs e)
{
    // This code executes in the UI thread, no problem to 
    // work with Controls like the ListView
    try
    {
        FileInfo file = e.UserState as FileInfo;
        imageList1.Images.Add(file.Name, Image.FromFile(file.FullName));
        ListViewItem item = new ListViewItem(file.Name);
        item.Tag = file.Name;
        item.ImageIndex = imageList1.Images.Count - 1;
        listView1.Items.Add(item);
     }
     catch(Exception ex)
     {
        ....
     }
}

答案 1 :(得分:0)

您可以像在主线程中那样在后台工作程序中执行所需的操作。但是每次要访问UI组件时,都需要在主线程(也称为UI线程)下进行。语法取决于您是否在WPF,Windows Phone,Xamarin等工作

使用WPF,你应该有这样的东西:

Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

答案 2 :(得分:0)

必须从UI线程而不是后台工作线程访问ListViewImageList

我建议您将初始化逻辑移至WatermarkPicker_Load,该foreach在UI线程中运行,然后将Control.Invoke循环更改为使用foreach (var file in dir.GetFiles()) { var image = Image.FromFile(file.FullName); var item = new ListViewItem(file.Name) { Tag = file.Name; } listView1.Invoke(() => { imageList1.Images.Add(file.Name, image); listView1.Items.Add(item); }); }

{{1}}