Question

I have a background worker that I use to create files in the background. I had it working so that the files were created and the UI was still responsive. I made some changes and now I can't figure out why the background worker is locking my main thread.

Here are my background worker methods. I don't have a progress changed event.

private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        if (filecreator_bgw.CancellationPending == true)
        {
            e.Cancel = true;
        }
        else
        {
            myManager.createFiles((SelectedFileTypes) e.Argument);
        }
    }

private void filecreator_bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                //status_label.Text = "Canceled!";
            }
            else if (e.Error != null)
            {
                //status_label.Text = "Error: " + e.Error.Message;
            }
            else
            {
                // Check the file manager object to see if the files were created successfully
                    status_label.Text = "COMPLETE";
                    file_statusLabel.Text = "Files Created: " + DateTime.Now.ToShortTimeString();
                    System.Threading.Thread.Sleep(5000);
                    status_label.Text = "Click Create Files to Begin";
                    createfiles_button.Enabled = true;
            }
        }

Here is the method to create the files.

public void createFiles(SelectedFileTypes x)
        {
            if (string.IsNullOrEmpty(Filename) || (x.isCSV == false && x.isTAB == false && x.isXML == false))
            {
                filesCreated = false;
                return;
            }

            // Declare the streams and xml objects used to write to the output files
            XDocument xmlFile;
            StreamWriter swCSV;
            StreamWriter swTAB;

            CSVFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "CSV_TEST.csv";
            swCSV = new StreamWriter(CSVFilename);

            TABFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "TAB_TEST.csv";
            swTAB = new StreamWriter(TABFilename);

            XMLFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "XML_TEST.csv";
            xmlFile = new XDocument(
                        new XDeclaration("1.0", "utf-8", "yes"),
                        new XComment("Crosswalk"));
            xmlFile.Add(new XElement("ACCOUNTS"));

            // String array for use when creating xml nodes
            string[] splits;

            // String used to read in a line from the input file
            string line = "";

            // Use a try and catch block, if any errors are caught, return false
            try
            {
                // Read each line in the file and write to the output files
                using (StreamReader sr = new StreamReader(Filename))
                {
                    int i = 0;
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (x.isCSV)
                        {
                            swCSV.WriteLine(line.Replace(delim, ","));
                        }
                        if (x.isTAB)
                        {
                            swTAB.WriteLine(line.Replace(delim, "\t"));
                        }
                        if (x.isXML)
                        {
                            if (i <= 0)
                            {
                                i++;
                                continue;
                            }

                            splits = line.Split(new string[] { delim }, StringSplitOptions.RemoveEmptyEntries);
                            xmlFile.Root.Add(
                                new XElement("ACCOUNTS",
                                    from s in header
                                    select new XElement(s, splits[Array.IndexOf(header, header.Where(z => z.Equals(s, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault())])
                                    )
                                );
                        }
                    }

                    // Dispose of all objects
                        swCSV.Close();
                        swCSV.Dispose();
                        swTAB.Close();
                        swTAB.Dispose();
                    if (x.isXML)
                    {
                        //xmlFile.Save(Path.GetFullPath(Filename) + Path.GetFileNameWithoutExtension(Filename) + "_TEST.xml");
                        xmlFile.Save(XMLFilename);
                    }
                }
            }
            catch (Exception)
            {
                filesCreated = false;
                return;
            }

            // Return true if file creation was successfull
            filesCreated = true;
        }

In the do work method, I build a simple struct to determine what output file types should be made and then I pass it to the method. If I comment out that call to create the files, the UI still does not respond.

In the create files method, I build out the files based on the input file that I am transforming. I do use a LINQ statement to help build out XML tags, but the arrays holding the tags values are small, 3-5 elements depending on the file chosen.

Is there a simple solution, or should I re-design the method. If I have to re-design, what are things I should keep in mind to avoid locking the main thread. Thanks

Here is how I call the runworkerasync method:

    private void createfiles_button_Click(object sender, EventArgs e)
    {
        SelectedFileTypes selVal = new SelectedFileTypes();
        foreach (var structVal in outputformats_checkedListBox.CheckedItems)
        {
            if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
                selVal.isCSV = true;
            if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
                selVal.isTAB = true;
            if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
                selVal.isXML = true;
        }

        // Call the FileManager object's create files method
        createfiles_button.Enabled = false;
        filecreator_bgw.RunWorkerAsync(selVal);
    }

UPDATE: I updated the call to start the worker and then the call to create the files using the argument passed into the worker.

Was it helpful?

Solution

You cannot interact with most UI controls directly from a BackgroundWorker. You need to access outputformats_checkedListBox.CheckedItems from the UI thread and pass the resulting SelectedFileTypes object into the BackgroundWorker as a parameter.

Also, pleas enote that your cancellation logic really didn't do much. In order for it to work well, you need to check CancellationPending throughout the process, not just when starting.

Here is a rough example of how you should start the worker:

private void StartWorker()
{
    SelectedFileTypes selVal = new SelectedFileTypes();
    foreach (var structVal in outputformats_checkedListBox.CheckedItems)
    {
        if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
            selVal.isCSV = true;
        if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
                selVal.isTAB = true;
        if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
            selVal.isXML = true;
    }
    filecreator_bgw.RunWorkerAsync(selVal);
}

private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
    SelectedFileTypes selVal = (SelectedFileTypes)e.Argument;
    myManager.createFiles(selVal);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top