82

I know it has 3 methods. In my program I have a method to send a message. It is often late and the program sometimes doesn't send the message at all in response to a button press. At times it is as late as 5 seconds from what I would expect and the program freezes. I want to use a BackgroundWorker to send the message as expected and allow the program to run normally at all times. I had the code for sending the message in a button handler. Now where do I put this equivalent code? I would like all of this to still be handled by a button press.

Is this the appropriate handler?

backgroundWorker1.RunWorkerAsync();

and in:

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

I'm going to put my code in the button handler? And this before:

carga.progressBar1.Minimum = 0;
carga.progressBar1.Maximum = 100;

Carga is my other form where the ProgressBar is. How do I use a BackgroundWorker in this scenario?

martijnn2008
  • 3,552
  • 5
  • 30
  • 40
angel
  • 4,474
  • 12
  • 57
  • 89

2 Answers2

109

You can update progress bar only from ProgressChanged or RunWorkerCompleted event handlers as these are synchronized with the UI thread.

The basic idea is. Thread.Sleep just simulates some work here. Replace it with your real routing call.

public Form1()
{
    InitializeComponent();

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

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

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(1000);
        backgroundWorker1.ReportProgress(i);
    }
}

private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}
Alex Aza
  • 76,499
  • 26
  • 155
  • 134
  • In your button1_Click() example - how do I get the data returned from the background worker back to the main (UI) thread? – niczak Apr 14 '17 at 21:01
  • Odd, I've got an exception about updating the UI (WPF progress bar) from my ProgressChanged method. Also, https://stackoverflow.com/questions/9732709/the-calling-thread-cannot-access-this-object-because-a-different-thread-owns-it/43455920 seems to have a similar issue to mine, and cites the need for `Application.Current.Dispatcher.Invoke()`. Any thoughts about this? – Bill Hoag Jun 05 '17 at 21:00
  • Why are we taking **object sender** parameter in almost every method. @Aza – Lahiru Gamage Apr 09 '18 at 08:08
  • 1
    @LahiruGamage It is required by DoWork, ProgressChanged etc and can be used to find out which object called the method. Check the documentation to find out more: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker.dowork?view=netframework-4.7.1 – Christian Seiler May 02 '18 at 14:11
  • Can you explain what the `+=` means in `backgroundWorker1.DoWork += backgroundWorker1_DoWork` ? – rayray May 14 '19 at 20:14
  • @rayray - the += operator is for operator overloading. In this case it means take backgroundWorker1.DoWork property (which is a function pointer) and add to it the name of the function that will be doing the work in background. Here is a link to the Microsoft Docs: [operator overloading](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading) – Su Llewellyn Jul 19 '19 at 15:47
  • @SuLlewellyn the docs you link to don't say anything about `+=` – CodyBugstein Jul 22 '19 at 15:56
  • 1
    @CodyBugstein Thanks, Cody! If you look further down, the Microsoft Docs specify: "Compound assignment operators cannot be explicitly overloaded. However, when you overload a binary operator, the corresponding compound assignment operator, if any, is also implicitly overloaded. For example, += is evaluated using +, which can be overloaded." – Su Llewellyn Jul 22 '19 at 17:50
81

I know this is a bit old, but in case another beginner is going through this, I'll share some code that covers a bit more of the basic operations, here is another example that also includes the option to cancel the process and also report to the user the status of the process. I'm going to add on top of the code given by Alex Aza in the solution above

public Form1()
{
    InitializeComponent();

    backgroundWorker1.DoWork += backgroundWorker1_DoWork;
    backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
    backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;  //Tell the user how the process went
    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.WorkerSupportsCancellation = true; //Allow for the process to be cancelled
}

//Start Process
private void button1_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}

//Cancel Process
private void button2_Click(object sender, EventArgs e)
{
    //Check if background worker is doing anything and send a cancellation if it is
    if (backgroundWorker1.IsBusy)
    {
        backgroundWorker1.CancelAsync();
    }

}

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(1000);
        backgroundWorker1.ReportProgress(i);

        //Check if there is a request to cancel the process
        if (backgroundWorker1.CancellationPending)
        {
            e.Cancel = true;
            backgroundWorker1.ReportProgress(0);
            return;
        }
    }
    //If the process exits the loop, ensure that progress is set to 100%
    //Remember in the loop we set i < 100 so in theory the process will complete at 99%
    backgroundWorker1.ReportProgress(100);
}

private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
         lblStatus.Text = "Process was cancelled";
    }
    else if (e.Error != null)
    {
         lblStatus.Text = "There was an error running the process. The thread aborted";
    }
    else
    {
       lblStatus.Text = "Process was completed";
    }
}
TheDanMan
  • 1,746
  • 1
  • 17
  • 22