Daniel Ethereal
Daniel Ethereal

Reputation: 95

C# BackgroundWorker ProgressChanged doesn't get fired until end of function

I have a method in my class that has some loops inside. Main purpose of this method is converting some files so I put a progressbar in my form that should get updated after each file has been converted.

I tried every possible combination and I read everything I could but I couldn't solve this issue.

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    converterProgressBar.Value = e.ProgressPercentage;
}

is called only after the main loop of my method has been executed.

This is my method:

public string Convert()
{
    convertBtn.Enabled = false;
    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;

    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    totalCount = files.length;
    bw.RunWorkerAsync();

    if (!Directory.Exists(folder))
    {
        Directory.CreateDirectory(folder);
    }
    foreach (string file in files)
    {
        countFile++;
        if (chk.Checked)
        {
            class1.DoJob();
        }

        using (// some code))
        {
            using (//some other code))
            {
                try
                {
                    using (// again some code)
                    {
                        // job executing
                    }
                }
                catch (exception
                {

                }   
            }
        }
        convertedVideosL.Text = txtToUpdate;
        convertedVideosL.Refresh();
    }
    countFile = countFile + 1;
    MessageBox.Show("Done");
    countFile = -1;
    return outputFile;
}

And here are the BackgroundWorker Event Handlers:

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i <= totalCount; i++)
    {
        if (bw.CancellationPending)
        {
            e.Cancel = true;
        }
        else
        {
            int progress = Convert.ToInt32(i * 100 / totalCount);
            (sender as BackgroundWorker).ReportProgress(progress, i);
        }
    }
}

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    converterProgressBar.Value = e.ProgressPercentage;
}

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled == false)
    {
        convertedVideosL.Text = "Finished!";
    }
    else
    {
        convertedVideosL.Text = "Operation has been cancelled!";
    }
}

But I cannot get to update the progress bar for every file that is converted. It waits for the foreach loop to end and then calls bw_ProgressChanged.

If I put RunWorkerAsync() inside the foreach loop an exception is thrown that says the BackgroundWorker is busy and cannot execute other tasks.

It seems to me obvious that DoWork() only executes a for loop then it shouldn't be aware of the conversion going on but ProgressChanged should be fired by ReportProgress(progress,i).

Could please someone explain me why and help me with a solution?

Thanks!

Upvotes: 0

Views: 981

Answers (1)

Currently the conversion is not executed by the instance of the BackgroundWorker type. The conversion should be called from the DoWork event handler.

Please consider extracting the conversion-related functionality:

if (!Directory.Exists(folder))
{
    Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
    // Details...
}

into the separate method. After that just call the method from the DoWork event handler.

Pseudo-code to demonstrate the idea:

public void StartConversion()
{
    ...
    TWorkerArgument workerArgument = ...;
    worker.RunWorkerAsync(workerArgument);
    // No message box here because of asynchronous execution (please see below).
}

private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{   
    // Get the BackgroundWorker that raised this event.
    BackgroundWorker worker = sender as BackgroundWorker;
    e.Result = Convert(worker, (TWorkerArgument)e.Argument);
}

private static TWorkerResult Convert(BackgroundWorker worker, TWorkerArgument workerArgument)
{
    if (!Directory.Exists(folder))
    {
        Directory.CreateDirectory(folder);
    }

    foreach (string file in files)
    {
        // Details...
        worker.ReportProgress(percentComplete);
    }

    return ...;
}

private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Show the message box here if required.
}

Please replace the TWorkerArgument and TWorkerResult types appropriately.

Also, please refer to the example which uses the BackgroundWorker class for the additional details: How to: Implement a Form That Uses a Background Operation, MSDN.

Upvotes: 1

Related Questions