I am having two functions - one is runs some commands on my serial port within a loop, the other one is an event handler that should stop the execution of the loop in the first method. Here is some sample code.
public void btm_Processing_Click(object sender, EventArgs e)
{
for (int i = 1; i <= x ; i++)
{
// My processing commands are here .
}
}
And here is my stop button event handler.
private void btm_Stop_Click(object sender, EventArgs e)
{
var dialogResult = MessageBox.Show("Do you want to stop processing?",
"Error",
MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
// Here is where I want to break that loop.
}
else
{
// Do other things.
}
}
I am not sure how I can do this.
The best way to do this would be to use a BackgroundWorker thread to execute your loop, then you can cancel it later from the main thread later.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
The easier way would be the make a global variable that you set
private volatile bool isWorking = false;
public void btm_Processing_Click(object sender, EventArgs e)
{
isWorking = true;
for (int i = 1; i <= x ; i++)
{
//My Processing Commands are Here
if(!isWorking)
break;
}
}
private void btm_Stop_Click(object sender, EventArgs e)
{
DialogResult dialogResult = MessageBox.Show("Do You Want To Stop Processing ? ", "Error", MessageBoxButtons.YesNo);
isWorking = dialogResult != DialogResult.Yes;
}
Set a break variable;
private volatile bool shouldStop = false;
private void btm_Processing_Click(object sender, EventArgs e)
{
for (int i = 1; i <= x ; i++)
{
//My Processing Commands are Here
if (shouldStop)
{
shouldStop=false;
break;
}
}
}
private void btm_Stop_Click(object sender, EventArgs e)
{
var dialogResult = MessageBox.Show("Do you want to stop processing?",
"Error",
MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
shouldStop = true;
}
else
{
// Do other things.
}
}
Related
I am having a problem . I want to use if statement to check if a button is clicked. For Example:
public void button1_Click(object sender, EventArgs e)
{
while (1)
{
...
...
...
if (Button2 == clicked)
{
break;
}
}
}
But it's not working like this, because the ".click" can only be on the left side of "+=" or "-=". Any idea how i can check if Button2 is clicked?
the code is loking like this: and i want to check button2 to stop the "programm".
the check for the Button2 is nearly at the end of the code ;)
public void button1_Click(object sender, EventArgs e)
{
Random rnd = new Random();
int EmFilterPos;
int ExFilterPos;
string String1;
int[] EmLB = new int[126];
int[] ExLB = new int[126];
int LBEmAnzahl = 0;
int LBEmTot = 0;
int LBExAnzahl = 0;
int LBExTot = 0;
UInt32 C_Zyklen;
UInt32 Zyklen;
Roche.DetectionControl2.Device_Filterwheels.ELBPowerState LB_On = Roche.DetectionControl2.Device_Filterwheels.ELBPowerState.LBOn;
Roche.DetectionControl2.Device_Filterwheels.ELBPowerState LB_Off = Roche.DetectionControl2.Device_Filterwheels.ELBPowerState.LBOff;
Roche.DetectionControl2.Device_Filterwheels.fiweGetLBResponse LightBarrier;
string Text = String.Format("Filterrad-Dauertest\r\nGestart am {0:d} um {0:t}\r\n\r\n", DateTime.Now);
System.IO.File.WriteAllText(#"TestLogFile\Filterrad_Dauertest1.txt", Text);
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweInitFilter();
System.Threading.Thread.Sleep(50);
while (Zyklen <= 20)
{
for (int q=1;q<8;q++)
{
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweMove(q,q);
System.Threading.Thread.Sleep(50);
Zyklen++;
}
for (int w=0;w<7;w++)
{
ExFilterPos = rnd.Next(1,8);
EmFilterPos = rnd.Next(1,8);
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweMove(ExFilterPos,EmFilterPos);
System.Threading.Thread.Sleep(50);
Zyklen++;
}
C_Zyklen = Zyklen;
if ((C_Zyklen % 2) < 14)
{
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweInitFilter();
System.Threading.Thread.Sleep(50);
using (System.IO.StreamWriter file = new System.IO.StreamWriter (#"TestLogFile\Filterrad_Dauertest1.txt", true))
{
file.Write("Init bei: ");
String1 = String.Format("{0,7}",Zyklen);
file.Write(String1);
file.Write(file.NewLine);
}
ExFilterPos = 60;
EmFilterPos = 60;
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweRawMove(ExFilterPos,EmFilterPos);
System.Threading.Thread.Sleep(50);
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweSetLB(LB_On);
while (EmFilterPos != -60)
{
LightBarrier = Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweGetLB();
if (LightBarrier.LBEm == Roche.DetectionControl2.Device_Filterwheels.ELBState.LBbright)
{
LBEmAnzahl++;
LBEmTot += EmFilterPos;
}
if (LightBarrier.LBEx == Roche.DetectionControl2.Device_Filterwheels.ELBState.LBbright)
{
LBExAnzahl++;
LBExTot += ExFilterPos;
}
ExFilterPos--;
EmFilterPos--;
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweRawMove(ExFilterPos,EmFilterPos);
}
EmFilterPos = LBEmTot / LBEmAnzahl;
ExFilterPos = LBExTot / LBExAnzahl;
using (System.IO.StreamWriter file = new System.IO.StreamWriter (#"TestLogFile\Filterrad_Dauertest1.txt", true))
{
file.Write("Nullstelle Mittelposition Em-Filter: ");
file.Write(EmFilterPos);
file.Write(file.NewLine);
file.Write("Nullstelle Mittelposition Ex-Filter: ");
file.Write(ExFilterPos);
file.Write(file.NewLine);
file.Write(file.NewLine);
}
Instrument.N1_DetectionControl2_1_Device_Filterwheels.fiweSetLB(LB_Off);
}
if (Button2 == clicked) // or something like this
break;
}
using (System.IO.StreamWriter file = new System.IO.StreamWriter (#"TestLogFile\Filterrad_Dauertest1.txt", true))
{
file.Write("Beendet am {0:d} um {0:t}\r\n", DateTime.Now);
}*/
}
Hm...
bool b1clicked = false, b2clicked = false;
public void button2_Click(object sender, EventArgs e)
{
b2clicked = true;
}
public void button1_Click(object sender, EventArgs e)
{
b1clicked = true;
if (b1clicked && b2clicked)
{
//...
}
}
Beside the weird behavior you want..and since you are not using Threads, you have the following options:
Local functions (.Net > 4.7)
private void B_Click(object sender, EventArgs e)
{
bool clickFlag = false;
void Click(object sender2, EventArgs e2)
{
clickFlag = true;
}
b2.Click += Click;
while (!clickFlag)
{
Thread.Sleep(1);
}
b2.Click -= Click;
//Continue with your stuff
}
Threads
Thread newThread;
private void Button1_Click()
{
newThread = new Thread(YourBreakableProcess);
newThread.Start();
}
private void Button2_Click()
{
newThread.Join();
}
private void YourBreakableProcess()
{
//Your breakable process
}
Async methods.
I hope you find a solution. Cheers.
Edit:
Since what you want is to interrupt the process of whatever you are doing, the only option you have is Local fuctions as shown above, if you are not tied to a specific framework version.
BackgroundWorker and check in every step if the button 2 was pressed with the flag thing mentioned in other answer.
Threads, and make a thread.Join when the button 2 is pressed.
Edit 2:
Updated answer with Threads, I will recommend that if you go with this option it is much better to use a BackgroundWorker instead as you will have the whole control of the process breaking it only in the place where it would be fine to break it.
You can achieve this using a flag variable. Declare and initialize flag value to false.On button2 click change flag value to true as follows,
private bool flag= false;
private void button2_Click(object sender, EventArgs e)
{
flag= true;
}
public void button1_Click(object sender, EventArgs e)
{
//Use flag to check whether button 2 has clicked or not
if (flag)
{
}
else
{
}
}
I added this two lines in the top of the Form1:
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
In the button click event start i added:
timer2.Enabled = true;
if (this.backgroundWorker1.IsBusy == false)
{
this.backgroundWorker1.RunWorkerAsync();
}
This is the DoWork event:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
if (filesContent.Length > 0)
{
for (int i = 0; i < filesContent.Length; i++)
{
File.Copy(filesContent[i], Path.Combine(contentDirectory, Path.GetFileName(filesContent[i])), true);
}
}
WindowsUpdate();
CreateDriversList();
GetHostsFile();
Processes();
}
Then the work completed event:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.Diagnose.Text = "THIS OPERATION HAS BEEN CANCELLED";
}
else if (!(e.Error == null))
{
this.Diagnose.Text = ("Error: " + e.Error.Message);
}
else
{
processfinish = true;
}
}
In the end the button click cancel event:
private void CancelOperation_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
When i click the cancel button i used a breakpoint i saw its going to the CancelAsync();
But then its just jumping to the timer2 tick event and keep working .
timer2 is starting to work once i clicked the start button.
This is the timer2 tick event:
private void timer2_Tick(object sender, EventArgs e)
{
timerCount += 1;
TimerCount.Text = TimeSpan.FromSeconds(timerCount).ToString();
TimerCount.Visible = true;
if (processfinish == true)
{
timer2.Enabled = false;
timer1.Enabled = true;
}
}
Why when i click the cancel button the operation is not stop and keep on going regular ?
And in the cancel button do i need to disposr/clean any objects or the backgroundworker somehow ?
This is what i did in the DoWork now:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
if (filesContent.Length > 0)
{
for (int i = 0; i < filesContent.Length; i++)
{
File.Copy(filesContent[i], Path.Combine(contentDirectory, Path.GetFileName(filesContent[i])), true);
}
}
WindowsUpdate();
CreateDriversList();
GetHostsFile();
Processes();
}
}
}
And the cancel button :
private void CancelOperation_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
timer2.Enabled = false;
}
But now in the DoWork i dont have the return;
So it never get to the completed event when i click the cancel button and never show the message this.Diagnose.Text = "THIS OPERATION HAS BEEN CANCELLED";
If i add now the return;
Then the rest of the code in the DoWork will be unreachable code
What to do then ?
Because your DoWork event checks the CancellationPending property before it starts doing all the heavy work.
The correct way is to check this property inside the loop.
Also note that if you're copying just a few, but very very large files, and want to cancel even while it is busy copying a file, you need to write out code that can be cancelled that does the copying as well.
You are checking the CancellationPending at the wrong stage.
Try something like ;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (filesContent.Length > 0)
{
for (int i = 0; i < filesContent.Length; i++)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
File.Copy(filesContent[i], Path.Combine(contentDirectory, Path.GetFileName(filesContent[i])), true);
}
}
if (!worker.CancellationPending)
WindowsUpdate();
if (!worker.CancellationPending)
CreateDriversList();
if (!worker.CancellationPending)
GetHostsFile();
if (!worker.CancellationPending)
Processes();
if (worker.CancellationPending)
e.Cancel = true;
}
I'm a bit new to using backgroundWorker and been doing some research online on how to use it. From the examples I'm getting there is something that doesn't make sense. In your DoWork function you run a loop from 0-100 or 1-10 and that loop basically tells your progress bar what your progress is, and inside that loop you do all your hard work. Well what if you have some job to do that is a loop, for example looping through a list and printing the values to a file?
Here's code that I've been playing with.
void m_oWorkder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
taskLabel.Text = "Task Cancelled";
}
else if (e.Error != null)
{
taskLabel.Text = "Error while performing background operation.";
}
else
{
taskLabel.Text = "Task Completed...";
}
startButton.Enabled = true;
cancelButton.Enabled = false;
}
void m_oWorkder_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
taskLabel.Text = "Processing ... " + progressBar1.Value.ToString() + "%";
}
void m_oWorkder_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
someRandom();
m_oWorker.ReportProgress(i);
if (m_oWorker.CancellationPending)
{
e.Cancel = true;
m_oWorker.ReportProgress(0);
return;
}
}
m_oWorker.ReportProgress(100);
}
private void startButton_Click(object sender, EventArgs e)
{
startButton.Enabled = false;
cancelButton.Enabled = true;
m_oWorker.RunWorkerAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
if (m_oWorker.IsBusy)
{
m_oWorker.CancelAsync();
}
}
private void someRandom()
{
int x = 0;
while (x != 100)
{
x++;
Console.WriteLine("Value of x = " + x);
}
}
There are different ways how to deal with this.
I prefer using the DoWorkEventArgs.Argument. So basically pass your list of fileNames to the BackGroundWorker:
void m_oWorkder_DoWork(object sender, DoWorkEventArgs e)
{
var fileNames = (list<string>) e.Argument;
foreach (var fileName in fileNames)
{
//write some stuff, do whatever you wish
m_oWorker.ReportProgress(fileNames.IndexOf(fileName));
}
}
In your DoWork function you run a loop from 0-100 or 1-10 and that loop basically tells your progress bar what your progress is, and inside that loop you do all your hard work.
This is a common pattern, but it's not compulsory.
You don't have to have a loop inside your DoWork method. You can do whatever you want, reporting progress whenever you want, and your ProgressChanged handler can interpret your progress reports however it wants, not necessarily as a percentage.
I am new in C#. I found some code which work on progressbar. What is does, when someone click on button start btnStartAsyncOperation_Click the progress bar starts increasing and when btnCancel_Click is pressed it cancel the operation. Here is the code
namespace BackgroundWorkerSample
{
public partial class Form1 : Form
{
BackgroundWorker m_oWorker;
public Form1()
{
InitializeComponent();
m_oWorker = new BackgroundWorker();
m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
m_oWorker.ProgressChanged += new ProgressChangedEventHandler(m_oWorker_ProgressChanged);
m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(abcd);
m_oWorker.WorkerReportsProgress = true;
m_oWorker.WorkerSupportsCancellation = true;
}
void abcd(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
lblStatus.Text = "Task Cancelled.";
}
else if (e.Error != null)
{
lblStatus.Text = "Error while performing background operation.";
}
else
{
lblStatus.Text = "Task Completed...";
}
btnStartAsyncOperation.Enabled = true;
btnCancel.Enabled = false;
}
void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
m_oWorker.ReportProgress(i);
if (m_oWorker.CancellationPending)
{
e.Cancel = true;
m_oWorker.ReportProgress(0);
return;
}
}
//Report 100% completion on operation completed
m_oWorker.ReportProgress(100);
}
private void btnStartAsyncOperation_Click(object sender, EventArgs e)
{
btnStartAsyncOperation.Enabled = false;
btnCancel.Enabled = true;
//Start the async operation here
m_oWorker.RunWorkerAsync();
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (m_oWorker.IsBusy)
{
//Stop/Cancel the async operation here
m_oWorker.CancelAsync();
}
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
private void lblStatus_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
}
}
}
Now I added 2 more button, button1 to pause and button2 to resume. Since I could not find any method to resume, I had to use CancelAsync() function when I press pause and I keep the value of progress bar in a global variable. Then when I press resume I start the progress bar again using RunWorkerAsync. But the problem is, I can not send the value of global variable in this function so it start from 0 progress.
I tried to use thread.sleep(infinite time here) when someone press pause and then stop the thread when someone press resume. Still the problem is, I can not press any button in this situation. Still if I enable button they don't work.
Please give me some solution.
You could try having your own variable, i.e
bool isPaused = false;
When someone clicks your pause button...
isPaused = true;
And set it to false when someone clicks resume. Finally, in your for loop in your doWork method, make it wait until that variable is false.
while (isPaused)
{
Thread.Sleep(100);
}
Let me know how this works out for you.
This is how I did it in my code:
In the backgroundWorker DoWork event I did:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_busy.WaitOne();
this.Invoke(new MethodInvoker(delegate { label2.Text = "Website To Crawl: "; }));
this.Invoke(new MethodInvoker(delegate { label4.Text = mainUrl; }));
webCrawler(mainUrl, levelsToCrawl, e);
}
Then in the pause button click event I did:
private void button4_Click(object sender, EventArgs e)
{
_busy.Reset();
}
In the resume button click event I did:
private void button5_Click(object sender, EventArgs e)
{
_busy.Set();
}
But it's not working when I click to start the process:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
button1.Enabled = false;
this.Text = "Processing...";
label6.Text = "Processing...";
label6.Visible = true;
button2.Enabled = false;
checkBox1.Enabled = false;
checkBox2.Enabled = false;
numericUpDown1.Enabled = false;
button3.Enabled = true;
}
Nothing happen only when I click the resume button the process start then when I click the pause button nothing happen.
I want that when I click the start process button it will start the backgroundWorker regular then when clicking the pause button it will pause and the resume button it will resume.
What did I do wrong ? Can someone fix my code ?
In your BackgroundWorker thread code, you need to find places that are safe to "pause" execution. The ManualResetEvent is the right way to code. This other post might help:
Is there a way to indefinitely pause a thread?
Basically, in a few choice points in your worker thread code where you want to allow it to pause, try inserting:
_busy.WaitOne(Timeout.Infinite);
And when you want to pause (from your main thread) you use:
_busy.Reset();
And to resume:
_busy.Set();
You should be able to do this using the ManualResetEvent like this ...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_busy.WaitOne();
test(mainUrl, levelsToCrawl, e);
}
... and then when you want to pause the thread call _busy.Reset() ... and when you want to restart it call _busy.Set().
Additionally, you can place _busy.WaitOne(); anywhere you want to pause.
I've been looking for the answer of this thread but I come up with my own solution i made and i just wanna share it with you. hope this works.
I have a background worker and i want to pause it when i hit close button of my form. asking "You are about to cancel the process" so it should pause the process.
declare bool pauseWorker = false; on your class.
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (condition)
{
if (pauseWorker == true)
{
while (pauseWorker == true)
{
if (pauseWorker == false) break;
}
}
else
{
//continue process... your code here
}
}
}
private void frmCmsnDownload_FormClosing(object sender, FormClosingEventArgs e)
{
if (bgWorker.IsBusy)
{
pauseWorker = true; //this will trigger the dowork event to loop that
//checks if pauseWorker is set to false
DiaglogResult x = MessageBox.Show("You are about cancel the process", "Close", MessageBoxButtons.YesNo);
if (x == DialogResult.Yes) bgWorker.CancelAsync();
else
{
e.Cancel = true;
pauseWorker = false; //if the user click no
//the do work will continue the process
return;
}
}
}
Therefore the main solution here is the boolean declaration that controls the DoWork event of BGWorker.
Hope this solution helps your problem. Thank you.
I use a simple class that utilizes System.Thread.Monitor and lock()...
public class ThreadPauseState {
private object _lock = new object();
private bool _paused = false;
public bool Paused {
get { return _paused; }
set {
if(_paused != value) {
if(value) {
Monitor.Enter(_lock);
_paused = true;
} else {
_paused = false;
Monitor.Exit(_lock);
}
}
}
}
public void Wait() {
lock(_lock) { }
}
}
Using it is very simple...
private ThreadPauseState _state = new ThreadPauseState();
private void btnPause_Click(object sender, EventArgs e) {
_state.Paused = true;
}
private void btnResume_Click(object sender, EventArgs e) {
_state.Paused = false;
}
private void btnCancel_Click(object sender, EventArgs e) {
backgroundWorker1.CancelAsync();
_state.Paused = false; // needed if you cancel while paused
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
for(var _ = 0; _ < 100; _++) {
_state.Wait();
if(worker.CancellationPending) return;
Thread.Sleep(100); // or whatever your work is
}
}
This works for me:
bool work = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += myChangeFunction;
backgroundWorker1.RunWorkerAsync();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true && work)
{
// Your code here
backgroundWorker1.ReportProgress(0);
Thread.Sleep(1000);
}
e.Cancel = true;
}
private void myChangeFunction(object sender, ProgressChangedEventArgs e)
{
// Here you can change label.text or whatever thing the interface needs to change.
}
private void Stop()
{
work = false;
}
private void Start()
{
work = true;
backgroundWorker1.RunWorkerAsync();
}
NOTE: If you want to change something of the interface, you have to put it in the myChangeFunction(), because in the DoWork() function will not work. Hope this helps.