Reputation: 788
I am trying to make a WinForms
app using .Net
.
I am using tutorial from here which shows BackgroundWorker
and ProgressBar
integration.
I added ProgressBar
and BackgroundWorker
controls to the form.
The names are the same as in example. Additionally, I set WorkerReportProgress
property to True
for BackgroundWorker
. No errors are shown and the project compiles successfully.
The problem is - progressbar does not move.
And yet, it moves when clicked manually... progressBar1.PerformStep();
.
What am I missing?
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();
}
}
}
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.
Upvotes: 0
Views: 632
Reputation: 9804
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.
Upvotes: 0
Reputation: 125197
After you made sure you attached the event handlers to ProgressChanged
and DoWork
:
progressBar1.PerformStep()
from DoWork
event handler.progressBar1.Value = e.ProgressPercentage;
in ProgressChanged
event handler.Upvotes: 1