0

Introduction

I am trying to make a WinForms app using .Net.

I am using tutorial from here wich shows BackgroundWorker and ProgressBar integration.

I added ProgressBar and BackgroundWorker controls to the form. The names are the same as in example. Additionaly, i set WorkerReportProgress property to True for BackgroundWorker. No errors are shown and project compiles sucessfully...

Problem

The problem is - progressbar does not move. And yet, it moves when clicked manually... progressBar1.PerformStep();.

What am i missing?

Code

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            // Start the BackgroundWorker.
            BackgroundWorker1.RunWorkerAsync();
        }

        private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100; i++)
            {
                // Wait 500 milliseconds.
                Thread.Sleep(500);
                // Report progress.
                BackgroundWorker1.ReportProgress(i);
            }
        }

        private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            progressBar1.PerformStep();
        }
    }
}

Update

Removed progressBar1.PerformStep(); from DoWork and ProgressChanged. Still the problem persists (ProgressBar does not move).

Thank you for ideas so far, will look into it more on Monday.

Rikijs
  • 679
  • 1
  • 7
  • 40
  • 1
    I would suggest trying wrapping the `progressBar1.PerformStep();` in a call to `Dispatcher.CurrentDispatcher.Invoke()` – Alex Feb 09 '18 at 13:09
  • 1
    1) you should **not** be updating the UI, in this case updating progress from within the worker thread. That's what `ReportProgress` is for. 2) why are you doing a `PerformStep` in both `DoWork` and your `ProgressChanged`? Do it in the latter only 3) don't use both `PerformStep` and `progressBar1.Value = xxx`. – MickyD Feb 09 '18 at 13:16
  • 1
    @Alex Incorrect. The whole point of `BackgroundWorker` is that it provides a separation between what is done in worker thread and how the UI is updated. Updating is the role of `ReportProgress`. Though you could have the background worker update some other aspect of the UI which would require invoking, this isn't the case as the OP is merely updating progress. You don't want to use `Invoke()` either, use `BeginInvoke`. The former can lead to a thread deadlock – MickyD Feb 09 '18 at 13:17
  • 3
    Subscribing the RunWorkerCompleted event is not optional, it is only way you can discover right now that the worker thread bombed on an IllegalOperationException. You must test e.Error. Calling progressBar1.PerformStep() from a worker thread is not legal. – Hans Passant Feb 09 '18 at 13:23

2 Answers2

1

After you made sure you attached the event handlers to ProgressChanged and DoWork:

  1. Remove progressBar1.PerformStep() from DoWork event handler.
  2. Then use just progressBar1.Value = e.ProgressPercentage; in ProgressChanged event handler.
Reza Aghaei
  • 103,774
  • 12
  • 145
  • 300
0

I wrote a simple Multithreading with Progress bar code a few years back. Hope it helps you:

#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
    if (!bgwPrim.IsBusy)
    {
        //Prepare ProgressBar and Textbox
        int temp = (int)nudPrim.Value;
        pgbPrim.Maximum = temp;
        tbPrim.Text = "";

        //Start processing
        bgwPrim.RunWorkerAsync(temp);
    }
}

private void btnPrimCancel_Click(object sender, EventArgs e)
{
    if (bgwPrim.IsBusy)
    {
        bgwPrim.CancelAsync();
    }
}

private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
    int highestToCheck = (int)e.Argument;
    //Get a reference to the BackgroundWorker running this code
    //for Progress Updates and Cancelation checking
    BackgroundWorker thisWorker = (BackgroundWorker)sender;

    //Create the list that stores the results and is returned by DoWork
    List<int> Primes = new List<int>();


    //Check all uneven numbers between 1 and whatever the user choose as upper limit
    for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
    {
        //Report progress
        thisWorker.ReportProgress(PrimeCandidate);
        bool isNoPrime = false;

        //Check if the Cancelation was requested during the last loop
        if (thisWorker.CancellationPending)
        {
            //Tell the Backgroundworker you are canceling and exit the for-loop
            e.Cancel = true;
            break;
        }

        //Determin if this is a Prime Number
        for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
        {
            if (PrimeCandidate % j == 0)
                isNoPrime = true;
        }

        if (!isNoPrime)
            Primes.Add(PrimeCandidate);
    }

    //Tell the progress bar you are finished
    thisWorker.ReportProgress(highestToCheck);

    //Save Return Value
    e.Result = Primes.ToArray();
}

private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pgbPrim.Value = e.ProgressPercentage;
}

private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    pgbPrim.Value = pgbPrim.Maximum;
    this.Refresh();

    if (!e.Cancelled && e.Error == null)
    {
        //Show the Result
        int[] Primes = (int[])e.Result;

        StringBuilder sbOutput = new StringBuilder();

        foreach (int Prim in Primes)
        {
            sbOutput.Append(Prim.ToString() + Environment.NewLine);
        }

        tbPrim.Text = sbOutput.ToString();
    }
    else 
    {
        tbPrim.Text = "Operation canceled by user or Exception";
    }
}
#endregion

Normally when writing a UI Element from a Alterante Thread, you have to use Invoke. BackgroundWorker is nice and Invoking the "ReportProgress" and "RunWorkerCompleted" Events on the thread that created it (wich should be the GUI thread) so you do not have to deal with that part of Multithreading wonkyness yet.

It is also nice enough to catch any Exceptions that would normally escape DoWork and be swallowed, exposing them to you in the Completed Event Args. Swallowing Exceptions is a huge issue with Multithreading.

The core issue is, that your loop breaks due to a Exception. Calling progressBar1.PerformStep(); inside the DoWork Event has to throw a "CrossThreadException". The BackgroudnWorker finishes (due to an exception) instantly. The RunWorker completed event is triggered when i was just the initial value.

Christopher
  • 8,956
  • 2
  • 14
  • 31