BackgroundWorker does not stop on CancelAsync() and works only once - c#

I've got one form called Sorter. There is the button 'jademy' on it which opens window 'Progress Window'
private void jademy_Click(object sender, EventArgs e)
{
ProgressWindow progress = new ProgressWindow();
progress.ShowDialog();
}
Code of 'Progress Window' form is following:
public partial class ProgressWindow : Form
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public ProgressWindow()
{
InitializeComponent();
stop.Visible = true;
ok.Visible = false;
backgroundWorker.RunWorkerAsync();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
#region block1
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
#endregion
}
private void stop_Click(object sender, EventArgs e)
{
backgroundWorker.CancelAsync();
}
private void ok_Click(object sender, EventArgs e)
{
this.Close();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
backgroundWorker.ReportProgress(i);
}
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
this.Text = "Done: " + e.ProgressPercentage.ToString() + "%";
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Cancelled", "Message", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
}
else if (!(e.Error == null))
{
MessageBox.Show("Error: " + e.Error.Message, "ERROR!", MessageBoxButtons.OKCancel);
}
else
{
ok.Visible = true;
stop.Visible = false;
}
}
}
Now. I have three problems.
Click on stop button does nothing. It seems that 'backgroundWorker.CancelAsync()' doesn't stop the process.
When I close progress window and I want to run it again I have to wait some time before click on 'jademy' button. Otherwise progress window is displayed like this:
(and nothing changes) instead of this: It looks like the program "remembers" that work was done even though it is a new instance of ProgressWindow. Notice that on the incorrect version 'OK' button is visible at once - instead of waiting for the completion of the work.
I would like to clarify the code in "block 1". To be honest I don't understand it fully. Is this part really essential or not? I mean, I've found a lot of examples (also on this forum - e.g. here), where this part wasn't included and users were reporting that the solution works. In my case, without this part progress bar didn't work at all, but maybe I've done something wrong.

Calling CancelAsync stops any pending work. But if the work has already started, the method body needs to check if cancel was called. See CancelAsync
CancelAsync submits a request to terminate the pending background
operation and sets the CancellationPending property to true.
When you call CancelAsync, your worker method has an opportunity to
stop its execution and exit. The worker code should periodically check
the CancellationPending property to see if it has been set to true.
I have no idea about it. By the way the images do not work. Embed it in the question.
The code assigns a method that is executed when the BackgroundWorker starts and you hook up methods to report the progress and do cleanup / updates once the background work is complete.

BackgroundWorker.CancelAsync is often misunderstood. It does not stop any pending work but is merely a signal to the UI thread that the work has been canceled! It just sets the CancellationPending property, which you can poll in the DoWork regularly.
Unfortunately the MSDN example with the Thread.Sleep calls in the DoWork is a very silly one. Normally you call a blocking operation in DoWork, which is often completely UI-independent.
See my answer here for a more usable example.

1.
According to MSDN BackgroundWorker Class page, maybe you should add a break to the loop.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
}
2.
Have no idea.
3.
The block 1 region is setting for BackgroundWorker event. In my case , it normally will appear at Form1.Designer.cs if I click the lightning icon in attribute to set the event.

Related

How to tell when a progressbar is done?

I am using C# with WinForms. I am updating a progressBar. When the Value reaches it's maximum value, I would like it to display a messageBox.
Is there anyway for the progressBar to execute a method when it's full? Is so then some code sample or a link to the solution would be appreciated
private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
//throw new NotImplementedException();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
}
// Back on the 'UI' thread so we can update the progress bar - and our label :)
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
int percentComplete = progressBarStatus.Value / progressBarStatus.Maximum;
labelPercentComplete.Text = percentComplete.ToString() + "% Completed";
//progressBarStatus.Value = e.ProgressPercentage;
//labelPercentComplete.Text = String.Format("Trade{0}", e.ProgressPercentage);
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(progressBarStatus.Value == progressBarStatus.Maximum)
{
MessageBox.Show("Test");
}
}
public void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler
(bgWorker_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += BackgroundWorkerProgressChanged;
}
It appears that you do not want to do something when background worker completes, but you want to do something when progress bar reaches maximum... Ok, first, set your progressBarStatus maximum value, then you should try something like this:
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (progressBarStatus.Maximum == e.ProgressPercentage)
{
// do whatever you want to do
}
}
and update progressBarStatus value from from another form.
Altough this may not the best way to do things, if this is really what you want, then do whatever makes you happy... :)
EDIT:
Ok, I added complete example of the program that works perfectly, with calling ProgressChanged event, and checking for the Maximum value correctly, when the Maximum value is reached, ProgressBar is getting restarted and the message is printed in the Output window, with bunch of comments (and bunch of typos of course :D ), please try this example, see how it works, and apply it to your problem.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace BGWORKERAPP
{
public partial class Form1 : Form
{
BackgroundWorker bgWorker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bgWorker.DoWork += bw_DoWork;
bgWorker.WorkerReportsProgress = true; // needed to be able to report progress
bgWorker.WorkerSupportsCancellation = true; // needed to be able to stop the thread using CancelAsync();
bgWorker.ProgressChanged += bw_ProgressChanged;
bgWorker.RunWorkerCompleted += bw_RunWorkerCompleted;
// ProgressBar is added to the form manually, and here I am just setting some initial values
progressBarStatus.Maximum = 100;
progressBarStatus.Minimum = 0;
progressBarStatus.Value = 0;
progressBarStatus.Step = 10;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
while (true) // keep looping until user presses the "Stop" button
{
if (bgWorker.CancellationPending) // if bgWorker.CancelAsync() is called, this CancelationPending token will be set,
{ // and if statement will be true
bgWorker.CancelAsync();
return; // Thread is getting canceled, RunWorkerCompleted will be called next
}
i++; // add any value you want, I chose this value because of the test example...
Thread.Sleep(1); // give thread some time to report (1ms is enough for this example) - NECESSARY,
//WITHOUT THIS LINE, THE MAIN THREAD WILL BE BLOCKED!
bgWorker.ReportProgress(i); // report progress (will call bw_ProgressChanged) - NECESSARY TO REPORT PROGRESS!
}
}
int somethingTerrible = 1; // used to do something terrible ;)
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// I added this "somethingTerrible" variable to make the ProgressChanged run all over again, even when e.ProgressPercentage value
// is greater then the progressBarStatus.Maximum, but, you should call bw.CancelAsync() because the job should be finished.
// Also, this code will give you Exception eventually, numbers are limited after all...
if (somethingTerrible * progressBarStatus.Maximum == e.ProgressPercentage)
{
Debug.WriteLine("THIS IS CALLED WHEN THE MAXIMUM IS REACHED"); // this will be printed in the Output window
progressBarStatus.Value = 0; // progressBarStatus value is at the maximum, restart it (or Exception will be thrown)
//bw.CancelAsync(); // used to stop the thread when e.ProgressPercentage is equal to progressBarMaximum, but in our
// example, we just make the code keep running.
// We should cancel bgWorker now because the work is completed and e.ProgressPercentage will
// be greater then the value of the progressBarStatus.Maximum, but if you really want
// you can do something like this to make the thread keep reporting without any errors (until numbers reach the limit)...
somethingTerrible++;
}
else
{
progressBarStatus.Value++; // increasing progressBarStatus.Value, until we get to the maximum.
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Worker completed"); // worker finished the task...
}
// Buttons are added to the Form manually as well
private void runBgTask_Click(object sender, EventArgs e) // button on the Form to start the thread
{
bgWorker.RunWorkerAsync(); // start the background worker (call DoWork)
}
private void stopBgTask_Click(object sender, EventArgs e) // button on the Form to stop the thread
{
bgWorker.CancelAsync(); // tell the background worker to stop (will NOT stop the thread immediately); the DoWork will be
// called once again, but with CancelationPending token set to true, so the if statement
// in the DoWork will be true and the thread will stop.
}
}
}
I guess you should look at BackgroundWorker which is made specifically for this purpose. You will get Event RunWorkerCompleted when your work in finished. I am giving you a working example where you are copying lots of files.
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.DoWork += BackgroundWorkerDoWork;
bgWorker.ProgressChanged += BackgroundWorkerProgressChanged;
bgWorker.RunWorkerCompleted += new BackgroundWorkerCompletedEventHandler
(bgWorker_RunWorkerCompleted);
void StartWork()
{
// Start BackGround Worker Thread
bgWorker.RunWorkerAsync();
}
void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
//NOTE : DONT play with the UI thread here...
// Do Whatever work you are doing and for which you need to show progress bar
CopyLotsOfFiles() // This is the function which is being run in the background
e.Result = true;// Tell that you are done
}
void CopyLotsOfFiles()
{
Int32 counter = 0;
List<String> filestobeCopiedList = ...; // get List of files to be copied
foreach (var file in filestobeCopiedList)
{
counter++;
// Calculate percentage for Progress Bar
Int32 percentage = (counter * 100) / filesCount;
bgWorker.ReportProgress(percentage);
// Files copy code goes here
}
bgWorker.ReportProgress(100);
}
void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Access Main UI Thread here
progressBar1.Value = e.ProgressPercentage;
}
private void BackgroundWorkerCompletedEventHandler(object sender, RunWorkerCompletedEventArgs e)
{
//Always check e.Cancelled and e.Error before checking e.Result!
//even though I'm skipping that here
var operationSuccessFul = Convert.ToBoolean(e.Result);
if(operationSuccessFul)
MessageBox.Show("I am Done");
}
You will get a call in BackgroundWorkerCompletedEventHandler function when you are done. You should display your progress bar in BackgroundWorkerProgressChanged event handler
You should think the other way round: Create a central "Progress class". This class is responsible for:
Update the progress bar
Show a MessageBox if certain conditions are met
Or to put it another way: its not the responibility of the progressbar to ... do something else than showing progress.

C#- background worker's CancelAsync() not working?

I want to abort the process but not able to do so, I am using Background worker with my functions of processing.
public void Init()
{
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
if (bw.CancellationPending == true)
{
e.Cancel = true;
}
else
{
e.Result = abd();
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
lbltext.content="Canceled";
}
else
{
lbltext.content="Completed";
}
}
private void btncan_Click(object sender, RoutedEventArgs e)
{
bw.CancelAsync();
}
private void btnstart_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
}
I am not able to abort the process using this code.
Function abd() is performing the processing part and returning the result.
Please provide me any solution.
Thanks.
When you call bw.CancelAsync() you just set CancellationPending flag to true. It does not cancels something by default. You need to handle pending cancellation manually. But you can't do that with your code, because when you click button, there are three possible options:
Long-running abd() method finished it's work and there is nothing to cancel
abd() started it's work, and background worker is blocked - it's waiting for results of abd(), then it continues execution - i.e. exits if-else block and raises RunWorkerCompleted event.
Nearly impossible option - you will be fast as light, and you will click button before if-else block entered. Than CancellationPending will be true, and abd() will not start execution
If you want to use cancellation, then do your long-running task in a loop, and check if cancellation is pending on each step:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
List<Foo> results = new List<Foo>();
// any loop here - foreach, while
for(int i = 0; i < steps_count; i++)
{
// check status on each step
if (bw.CancellationPending == true)
{
e.Cancel = true;
return; // abort work, if it's cancelled
}
results.Add(abd()); // add part of results
}
e.Result = results; // return all results
}
Probably DoWork may have finished its work before calling CancelAsync and as mentioned in the docs e.Cancelled may be false..
Docs say this
Be aware that your code in the DoWork event handler may finish its
work as a cancellation request is being made, and your polling loop
may miss CancellationPending being set to true. In this case, the
Cancelled flag of System.ComponentModel.RunWorkerCompletedEventArgs in
your RunWorkerCompleted event handler will not be set to true, even
though a cancellation request was made. This situation is called a
race condition and is a common concern in multithreaded programming.
How about the following?
While(!bw.CancellationPending)
{
//do some work!
}
e.Cancel = true;

Button Text, Inconsistent

Why does my first attempt to change a button's text in this code not work, while the third attempt does work ?
My user will have to wait a few seconds while the serial port connects. After that, I want to alert him that he has already connected (a second attempt can cause problems).
I wanted to let him know that things are okay, so he won't think "duh" and click twice.
Fail. The text change never appears.
Okay, why does the third change in button text work, but the first one does not ? I don't know if the second one works or not.
/***********************************************************************
* Button: Connect Port *
***********************************************************************/
private void TheConnectPortButton_Click(object sender, EventArgs e)
{
string OldText = TheConnectPortButton.Text;
TheConnectPortButton.Text = "Busy, Please Wait"; /////// This never happens
ButtonBoss.ButtonHandler_ConnectPort();
TheConnectPortButton.Text = OldText; /////// Not sure if this happens
if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
{
TheConnectPortButton.Text = "Connected"; /////// This one does happen
}
}
the aUartSemaphoreThatTells.IfWeHaveConnectedToAPort is also used by the ButtonBoss routine to make sure he doesn't connect a second time, along with other button routines (e.g., make sure we are connected before we Tx/Rx or whatever).
I tried changing the code after the routine returns to look like this...
if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
{
TheConnectPortButton.Text = "Connected";
}
else
{
TheConnectPortButton.Text = OldText;
}
...and I still get the same result.
My guess (and that's all it is) is that threading is somehow involved in all this, and that the serial port routines trump the button text changing routines by some convolution that I don't follow properly at the moment.
Question: What do I need to do to get the text to change before the connection stuff hogs the system ?
(If that's what's happening)
Question 2: If I can't make this happen, I think I've read about "greying out" the buttons, or, I believe I saw somewhere that I can actually make a button go away right before the user's eyes so that he can't click it again. Links to example code would be welcome.
The problem is you're doing everything from one and the same event-handler consequently, so that the button has no time to get updated (redrawn). You could call Application.DoEvents(); method, but it's not a good idea at all, please, read Use of Application.DoEvents()
I think usually you're expected to push a time-consuming task into a separate thread, get progress report from it and update your GUI. There is a plenty of ways to create a "worker" thread and get some respond from it. For example, use a BackgroundWorker Class:
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker w = new BackgroundWorker();
w.WorkerReportsProgress = true;
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.ProgressChanged += new ProgressChangedEventHandler(w_ProgressChanged);
w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
w.RunWorkerAsync();
button1.Text = "Started";
}
//may influence GUI, as this event handler is run on the GUI thread
void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Text = "Job is done";
}
//may influence GUI, as this event handler is run on the GUI thread
void w_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
button1.Text = e.ProgressPercentage.ToString();
}
//runs in the worker thread...should do the actual job
//may influence GUI through `ReportProgress`
//or through `Invoke` method
void w_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(500);
worker.ReportProgress(10 * i);
}
}
}
Or you may use Task Class:
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e)
{
new Task(DoTask).Start();
}
void DoTask()
{
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(500);
//here you need to update GUI through `Invoke` method
//as the GUI may only be influenced from the the thread,
//where it's created
this.Invoke(new Action<int>((j) =>
{
button1.Text = j.ToString();
}), 10 * i);
}
}
}

Program doesn't increment progress bar while waiting for thread to exit

I have a c# form app that serves as an UI and executes an external exe. I want to make a progress bar increment until the external exe finishes executing. so i have the following code:
// create thread and Start external process
Thread MyNewThread = new Thread(new ThreadStart(startmodule));
MyNewThread.Start();
do
{
if (progressBar1.Value < 100)
{
progressBar1.Value++;
}
} while (MyNewThread.IsAlive);
label5.Text = "Status: Done";
// startmodule()
void startmodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
ProcessObj.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
ProcessObj.Start();
}
Instead it fills the bar up instantly and shows "Done" message but the external exe (AppPath) still runs in the background.
Please post some ideas im stuck. i don't know whats wrong. Thank you for your time.
You cannot make this work, you cannot guess how long the process will take. Set the ProgressBar.Style property to Marquee. Set it Visible property to true when you start the process. Use the Process.Exited event to set it back to false. Like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void ButtonRunProcess_Click(object sender, EventArgs e) {
var ProcessObj = new Process();
ProcessObj.SynchronizingObject = this;
ProcessObj.EnableRaisingEvents = true;
ProcessObj.Exited += new EventHandler(ProcessObj_Exited);
ProcessObj.StartInfo.FileName = #"c:\windows\notepad.exe";
// etc...
ProcessObj.Start();
progressBar1.Visible = true;
}
void ProcessObj_Exited(object sender, EventArgs e) {
progressBar1.Visible = false;
}
}
Well the loop is being run so fast, that it reaches 100% before your task is actually completed. The condition that the loop is being check for (The thread being alive) is going to be true until your task is completed, but the loop is causing the progress bar to fill up prematurely.
In order to run a progress bar you have to be able to quantify the progress of the long running task. You have nothing in the code that attempts to quantify this.
You would need there to be communication between the two processes in order to make this progress bar work well. In other words the external process needs to send messages back to the parent app informing the parent app of the measure of progress. Now, that can be hard to achieve so a marquee style progress bar may be more appropriate.
Finally i got some "free" time to test the backgroundworker as suggested above. i can say it's the best solution and it doesn't freeze the UI. Example implementation follows:
preparemodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
}
void run_Click(object sender, EventArgs e)
{
preparemodule();
backgroundWorker1.RunWorkerAsync(ProcessObj);
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int i=0;
ProcessObj.Start();
while (checkifexists("notepad", 0) == true)
{
i++;
label5.Text = "Status: notepad running... " + progressBar1.Value.ToString() + "%";
Thread.Sleep(3000);
backgroundWorker1.ReportProgress(i);
if ((backgroundWorker1.CancellationPending == true))
{
e.Cancel = true;
}
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage <= 100)
{
progressBar1.Value = e.ProgressPercentage;
}
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label5.Text = "Status: Done";
}
void cancel_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
As you see we can even cancel it. and by checking if notepad is running we can increment out progressbar. Dont forget to enable bgWorker's "reportsprogress" and "supportscancellation" properties somewhere in your code. i hope it helps someone.
First, #Tim answer is right about what is happening.
If you can control the external app, make a way to it communicate with the main process telling the current state and update the progress bar according to these messages.
If is not possible, try to estimate the execution time and set the progress according to the execution time. This is valid if it performs always in same time for the same task.
Background worker thread was designed for this sort of thing.
It has an event you can fire while processing something, you handle it and update your progress bar. Course as noted by others you don't seem to have any measure of progress, just some time has passed, so it's not really an indication of progress you want but some sort of "I'm busy" animation, if you use a progress bar for that you get all sorts of issues that drive the UI boys mad, like it never gets to 100%, or it gets to 100% well before the operation has finished, or even cycles round.
So if you can indicate some progress from the thread, e.g if you are looping through X items fire the progress event every 10% of X. Use a Background worker thread.
If you can't don't use a progress bar kick the thread off an make some animated control visible. When the thread finishes make the animation invisible again. What and how of the animation is up to you and your UI boys.

Restart background worker

Is there a way to directly "restart" a background worker?
Calling CancelAsync() followed by RunWorkerAsync() clearly won't do it as their names imply.
Background info:
I have a background worker which calculates a total in my .net 2.0 Windows Forms app.
Whenever the user modifies any value which is part of this total I'd like to restart the background worker in case it would be running so that directly the latest values are considered.
The backgriound work itself does not do any cancleing.
When you call bgw.CancelAsync it sets a flag on the background worker that you need to check yourself in the DoWork handler.
something like:
bool _restart = false;
private void button1_Click(object sender, EventArgs e)
{
bgw.CancelAsync();
_restart = true;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 300; i++)
{
if (bgw.CancellationPending)
{
break;
}
//time consuming calculation
}
}
private void bgw_WorkComplete(object sender, eventargs e) //no ide to hand not sure on name/args
{
if (_restart)
{
bgw.RunWorkerAsync();
_restart = false;
}
}
There are a couple of options, it all depends on how you want to skin this cat:
If you want to continue to use BackgroundWorker, then you need to respect the model that has been established, that is, one of "progress sensitivity". The stuff inside DoWork is clearly required to always be aware of whether or not the a pending cancellation is due (i.e., there needs to be a certain amount of polling taking place in your DoWork loop).
If your calculation code is monolithic and you don't want to mess with it, then don't use BackgroundWorker, but rather fire up your own thread--this way you can forcefully kill it if needs be.
You can hook the change event handler for the controls in which the values are changed and do the following in the handler:
if(!bgWrkr.IsBusy)
//start worker
else if(!bgWrkr.CancellationPending)
bgWrkr.CancelAsync();
Hope it helps you!
I want to leave my requests running, but no longer care about the results. I override the value of the background worker (my busy spinner is using the isBusy flag).
private void SearchWorkerCreate() {
this.searchWorker = new BackgroundWorker();
this.searchWorker.DoWork += this.SearchWorkerWork;
this.searchWorker.RunWorkerCompleted += this.SearchWorkerFinish;
}
private void SearchWorkerStart(string criteria){
if(this.searchWorker.IsBusy){
this.SearchWorkerCreate();
}
this.searchWorker.RunWorkerAsync(criteria);
this.OnPropertyChanged(() => this.IsBusy);
this.OnPropertyChanged(() => this.IsIdle);
}
May this method help someone... I've created a function to reset the backgroundworker in one method. I use it for task to do periodically.
By creating a Task, the backgroundworker is can be stopped with the CancelAsync and restarted inside the Task. Not making a Task wil start the backgroundworker again before it is cancelled, as the OP describes.
The only requirement is that your code runs through some loop, which checks the CancellationPending every period of time (CheckPerMilliseconds).
private void ResetBackgroundWorker()
{
backgroundWorker.CancelAsync();
Task taskStart = Task.Run(() =>
{
Thread.Sleep(CheckPerMilliseconds);
backgroundWorker.RunWorkerAsync();
});
}
Inside the backgroundworker I use a for-loop that checks the CancellationPending.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(true)
{
if (backgroundWorker.CancellationPending)
{
return;
}
//Do something you want to do periodically.
for (int i = 0; i < minutesToDoTask * 60; i++)
{
if (backgroundWorker.CancellationPending)
{
return;
}
Thread.Sleep(CheckPerMilliseconds);
}
}
}

Categories

Resources