Search code examples
c#multithreadinglistviewbackgroundworker

C# BackgroundWorker to access ListViewItem


I'm sure this has been asked millions of times before but I'm banging my head against a brick wall at the moment. First I will give you an overview of my goals:

I am creating a media conversion application using C# in Visual Studio 2010. I have successfully managed to convert from MP3, WAV, WMA and OGG into the AIFF file format using NAudio, however that time has come where I need to use BackgroundWorker to convert the files as the program locks up during the conversion process, and I want the progress text to be updated while they are converting. The selected media files will be in a ListView box and I use a foreach loop to run through the ListView and then convert the files individually.

The problem I am facing is accessing the ListView from a seperate thread. The error I get in debugging is:

Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on

Here is my code to convert the files:

        foreach (ListViewItem item in listView1.Items)
        {
            worker.ReportProgress((i * listView1.Items.Count));
            if(item.Text.EndsWith(".mp3"))
            {
                using (Mp3FileReader mp3 = new Mp3FileReader(item.Text))
                {
                    using (WaveStream pcm = WaveFormatConversionStream.CreatePcmStream(mp3))
                    {
                        AiffFileWriter.CreateAiffFile(item.SubItems[2].Text, pcm);
                    }
                }
                string[] saLvwItem = new string[3];
                saLvwItem[0] = item.SubItems[2].Text;
                saLvwItem[1] = "Conversion completed";
                ListViewItem lvi = new ListViewItem(saLvwItem);
                listView2.Items.Add(lvi);

            } 
            else
            {
                if (item.Text.EndsWith(".wav"))
                {
                    using (WaveFileReader wav = new WaveFileReader(item.Text))
                    {
                        using (WaveStream pcm = WaveFormatConversionStream.CreatePcmStream(wav))
                        {
                            AiffFileWriter.CreateAiffFile(item.SubItems[2].Text, pcm);
                        }
                    }
                    string[] saLvwItem = new string[3];
                    saLvwItem[0] = item.SubItems[2].Text;
                    saLvwItem[1] = "Conversion completed";
                    ListViewItem lvi = new ListViewItem(saLvwItem);
                    listView2.Items.Add(lvi);
                }
                else
                {
                    if (item.Text.EndsWith(".wma"))
                    {
                        using (WMAFileReader wma = new WMAFileReader(item.Text))
                        {
                            using (WaveStream pcm = WaveFormatConversionStream.CreatePcmStream(wma))
                            {
                                AiffFileWriter.CreateAiffFile(item.SubItems[2].Text, pcm);
                            }
                        }
                        string[] saLvwItem = new string[3];

                        saLvwItem[0] = item.SubItems[2].Text;
                        saLvwItem[1] = "Conversion completed";
                        ListViewItem lvi = new ListViewItem(saLvwItem);
                        listView2.Items.Add(lvi);
                    }
                    else
                    {
                        string[] saLvwItem = new string[3];
                        saLvwItem[0] = item.SubItems[2].Text;
                        saLvwItem[1] = "Not converted. Unsupported format";
                        ListViewItem lvi = new ListViewItem(saLvwItem);
                        listView2.Items.Add(lvi);
                    }
                }
            }
        }

Which needs to be called from the backgroundWorker1_DoWork event.

Could anyone tell me how I can get this working?

Thanks in advance...


Solution

  • If you want to call form controls in different thread then all you need to do is this:

    this.Invoke(new MethodInvoker(delegate { ListView1.Enable = true; }));

    Look at ListView1.Enable = true; in the above line. Replace it with what ever you trying to do with the ListView1. Another example:

    this.Invoke(new MethodInvoker(delegate { ListView1.Visible = true; }));

    ListView listview2 = null; 
    this.Invoke(new MethodInvoker(delegate { listview2 = listview1; }));
    foreach (ListViewItem item in listView2.Items)
    {
        worker.ReportProgress((i * listView2.Items.Count));
        .
        .
        .//Replace listview1 with listview2 in the remaining code
        .//your rest of the code
    

    Hope that helps.