This is code freezing current window. How to made non freezing this form.
public partial class Form1 : Form
{
Thread t;
int s = 0;
public Form1()
{
InitializeComponent();
label2.Text = "Push the Button";
button1.Text = "Push me!";
button1.Click += new EventHandler(button1_Click);
this.Controls.Add(label2);
this.Controls.Add(button1);
}
void button1_Click(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(RunMe));
t.Start();
}
private void RunMe()
{
if (!InvokeRequired)
{
while(true)
{
label2.Text = s.ToString();
s++;
Task.Delay(10000).Wait(10000);
}
}
else
{
Invoke(new ThreadStart(RunMe));
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
As others have stated, you are using the UI thread to execute an endless loop, you should use the Timer control, it was built for exactly what you're doing.
https://social.msdn.microsoft.com/Forums/windows/en-US/43daf8b2-67ad-4938-98f7-cae3eaa5e63f/how-to-use-timer-control-in-c?forum=winforms
Your code can benefit from using async-await if you are using .net 4.5. Using await you won't have to start a seperate thread for your RunMe method, it will free up your UI thread to do other work but the SynchronizationContext is captured so that you don't have to use Invoke to update the UI. For how that works, see this blog.
I think you should be able to rewrite your code like this:
async void button1_Click(object sender, EventArgs e)
{
// kicks off the RunMe method and returns
await RunMe();
}
private Task RunMe()
{
while(true)
{
label2.Text = s.ToString();
s++;
await Task.Delay(10000);
}
}
Despite the infinite while loop the method only wakes up to update the label and runs for a very short time in the UI thread.
You should call the invoke only for updating the label like this:
while(true)
{
if (!InvokeRequired)
{
label2.Text = s.ToString();
}
else
{
Invoke(new Action(()=>{label2.Text = s.ToString();}));
}
s++;
Task.Delay(10000).Wait(10000);
}
Related
In a WinForm application, I have an On/Off switch for a background process.
On Click, the program launch a process and restart it when it's finish, till you use the Off switch.
The following code is the working attempt that had multiple issues.
From Damien_The_Unbeliever comments:
suspend threads which means that they stay around forever, and implementing looping via recursion which could easily cause a stack overflow.
public partial class frmMain
{
Thread thread;
bool isRunning = false;
public frmMain()
{
InitializeComponent();
}
private void OnOffSwitch_Click(object sender, EventArgs e)
{
if (!isRunning)
{
btnSwitch.Text = "Stop";
isRunning = true;
thread = new Thread(doLoop);
thread.IsBackground = true;
thread.Start();
}
else
{
if (thread.IsAlive)
thread.Suspend();
btnSwitch.Text = "Start";
isRunning = false;
}
}
public void doLoop()
{
ClearScreenLogic.Run();
if (AutoReconnect)
ReconnectLogic.Run();
// Etc..
doLoop();
}
I am trying to switch from this working solution to background worker.
Implement your doLoop in the DoWork event of the BackGroundWorker and make sure you handle cancelation. Make sure to set the properties of your backgroundworker to WorkerReportprogress and WorkerSupportCancellation to true;
This is what you would need:
private void button1_Click(object sender, EventArgs e)
{
// on and off
if (backgroundWorker1.IsBusy)
{
// cancel if we have not already done so
if (!backgroundWorker1.CancellationPending)
{
backgroundWorker1.CancelAsync();
}
}
else
{
// start the background work
button1.BackColor = Color.Yellow;
backgroundWorker1.RunWorkerAsync();
}
}
// this runs on a background thread
// do not do stuff with the UI here
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int progress = 0;
// stop looping if cancellation is requested
while (!backgroundWorker1.CancellationPending)
{
// make it nice
backgroundWorker1.ReportProgress(progress);
ClearScreenLogic.Run();
if (AutoReconnect)
ReconnectLogic.Run();
// Etc..
progress++; // for feedback
}
}
// tell the use something is going on, this runs on the UI thread
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
}
// we're done, tell the user so
// this runs on the UI thread
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.BackColor = Color.Green;
label1.Text = "cancelled";
}
When implemented correctly your users will see something like this:
This question already has answers here:
c# Thread issue using Invoke from a background thread
(5 answers)
Closed 6 years ago.
I can't seem to be able to kill my thread in C#. The program seems to get stuck in an infinite loop on the FormClosing event.
EDIT // I'm attempting to end the thread and close the whole program when the FormClosing event gets fired.
Here's the code:
public partial class Form1 : Form
{
private Thread thread;
private volatile bool threadRunning = true;
public Form1()
{
InitializeComponent();
}
private void Loop()
{
Console.WriteLine(threadRunning);
while (threadRunning)
{
MethodInvoker mi = delegate { timeLabel.Text = TimeWriterSingleton.Instance.OutputTime(); };
Invoke(mi);
}
}
private void Form1_Load(object sender, EventArgs e)
{
thread = new Thread(Loop);
thread.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}
}
Your Join blocked the GUI thread, and your Invoke in the other thread is waiting for your GUI thread to process the delegate.
A quick fix would be to use BeginInvoke instead of Invoke, thus posting rather than sending the window message.
Alternatively, don't join. The purpose of that code is to clean up after yourself, why do you care when the thread dies?
A 3rd fix would be to just gut the thread, either through Thread.Abort or Environment.Exit. It might skip some clean up, but your particular code shouldn't care and the point is to exit anyway.
Edit: working code using BeginInvoke follows:
private void Loop()
{
while (threadRunning)
{
BeginInvoke(new MethodInvoker(() => timeLabel.Text = DateTime.Now.ToString()));
Thread.Sleep(100);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}
The issue with the original code is that it's running as fast as your CPU allows, filling the message queue to the point where the GUI thread can't keep up. Updating Windows controls is very expensive, compared to simply adding a number to a queue. So I added a pause between UI updates to let the GUI thread breathe.
To the downvoters, I'd be curious why you're doing it. Nothing I said is factually wrong.
I decided to switch to using a timer. The code now looks like this, and the application works:
public partial class Form1 : Form
{
private System.Timers.Timer timer;
public Form1()
{
InitializeComponent();
timer = new System.Timers.Timer(60000);
}
private void Form1_Load(object sender, EventArgs e)
{
timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
timer.Elapsed += TimerElapsed;
timer.Enabled = true;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
}
}
Actually using the BeginInvoke() is not bad idea. It might look like that:
private void Form1_Load(object sender, EventArgs e)
{
thread = new Thread(() => Loop(this));
thread.Start();
}
private void Loop(Form1 form)
{
while (threadRunning && !form.IsDisposed)
{
MethodInvoker mi = delegate() { timeLabel.Text = /* Some text */ ; };
BeginInvoke(mi);
// Let sleep some time...
Thread.Sleep(1);
}
}
private void Form1_FormClosing_1(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}
I decided to use this third-party component to make a simple loading control in my windows form.
http://www.codeproject.com/Articles/14841/How-to-write-a-loading-circle-animation-in-NET
This works fine when turns on and off changing the property "Active" to true or false in a single request (one per time). The problem is when a process is waiting to be served, and I pretend to Active the loadingControl before the process starts and turn off when I "think" that the process has to be finished. When I do it, the image loading is shown as a static image. (Without animation).
I'm sorry for this question, I'm new in C#. But I think that I need to use Threads or something similar.
So my general code is this:
using [libraries here]...;
namespace [namespace here]
{
Public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.loadingCircle1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(showLoading));
this.loadingCircle1.Visible = true;
t.Start();
//Import an Excel
t.Abort();
}
public void showLoading()
{
loadingCircle1.Active = true;
loadingCircle1.RotationSpeed = 10;
}
}
}
But Always the Loading shows as a static image without the animation.
You create a thread, which simply sets two properties and then ends. The t.Abort will probably do nothing, since the thread will have been exited by that time. Even worse, you import the excel file on the UI thread, which blocks any animation and freezes the complete UI.
This is how you should make it:
Remark: Of course if your form is responsive, you must disable/enable the controls and prepare to the case what happens if your form is being closed during the load.
1. Using threads
If you really want to explicitly use threads, do it like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread workerThread = null;
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation (I used a progress bar, start your circle here)
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job and the timer, which polls the thread
btnImport.Enabled = false;
workerThread = new Thread(LoadExcel);
workerThread.Start();
timer1.Interval = 100;
timer1.Start();
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (workerThread == null)
{
timer1.Stop();
return;
}
// still works: exiting
if (workerThread.IsAlive)
return;
// finished
btnImport.Enabled = true;
timer1.Stop();
progressBar1.Visible = false;
workerThread = null;
}
}
2. Background worker
The BackgroundWorker can throw an event when it is finished:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job
btnImport.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
LoadExcel();
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
3. Using async-await
This is the simplest one.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void btnImport_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// simply start and await the loading task
btnImport.Enabled = false;
await Task.Run(() => LoadExcel());
// re-enable things
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
I'd recommend to use async/await (for C# 5.0):
private void button1_Click(object sender, EventArgs e){
ImportAsync();
}
private async Task ImportAsync(){
// UI-thread
showLoading();
this.loadingCircle1.Visible = true;
// wait until task will be finished
await Task.Run(() => {
// different non-blocking thread for all the hard work, but without UI-stuff
// import an Excel
});
// going back to UI-thread
this.loadingCircle1.Visible = false;
}
I have a loop that I would like to stop using a button.
Edited for better understanding:
I do realize that you cannot stop a button while a loop was running since it will not work as long as that current UI is running. What I'm really asking for is the most efficient way of creating a thread or using BGWorker to stop this. I have seen some methods, but most of them are for Java and not C#.
What I would like to do is:
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums; i++)
{
doSomething();
}
}
private void stop_Click(object sender, EventArgs e)
{
stops start_Click()
}
You can't do that. For starters, the for loop is running synchronously on the UI thread, which means you won't even be able to click the "Stop" button.
Hence, you need to move the operations of the for loop onto another thread, which means you likely won't be using a for loop at all. You need to think about how the code inside actually needs to be executed, then based on how you are doing the processing, you can implement the "Stop" button.
A very simple way to do this would be to just:
new Thread(() =>
{
int i = 0;
while (!stop && i < num)
{
doSomething();
i++;
}
}).Start();
And set stop to stop the processing loop. In a more realistic scenario, you could queue up functions that you want to process, then stop dequeuing via a similar method. Unfortunately, its hard to reccommend a setup without knowing more details.
Any solution based on your code will also have the problem of the current doSomething() completing execution (which could take a while). Again, without more info, its hard to say what the best approach to fixing that is.
To keep your UI responsive to be able to cancel the running operation you can use a backgrounworker.
The backgroundworker does the work in an other thread while keeping your UI responsive:
private readonly BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
_backgroundWorker.DoWork += backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
Disposed += Form1_Disposed;
}
private void Form1_Disposed(object sender, EventArgs e)
{
_backgroundWorker.Dispose();
}
private void StartLoop()
{
if ( !_backgroundWorker.IsBusy )
{
_backgroundWorker.RunWorkerAsync();
}
}
private void StopLoop()
{
_backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork( object sender , DoWorkEventArgs e )
{
var backgroundWorker = ( BackgroundWorker ) sender;
for ( var i = 0; i < 100; i++ )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Do Work
}
}
private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
if ( e.Cancelled )
{
// handle cancellation
}
if ( e.Error != null )
{
// handle error
}
// completed without cancellation or exception
}
IMHO, it's likely the best approach here is to convert your work to an asynchronous operation and then use the async/await idiom for the loop. E.g.:
private bool _stopLoop;
private async void start_Click(object sender, EventArgs e)
{
_stopLoop = false;
for(int i = 0; i < nums && !_stopLoop; i++)
{
await Task.Run(() => doSomething());
}
}
private void stop_Click(object sender, EventArgs e)
{
_stopLoop = true;
}
This allows the loop itself to execute in the UI thread where the _stopLoop variable is being managed, but without actually blocking the UI thread (which among other things would prevent the "Stop" button from being clicked).
Unfortunately, you didn't provide details about how doSomething() works. It's possible there's a good way to convert that whole method to be an async method, but I can't comment on that without the actual code.
Note that this approach will only interrupt the loop at a point in between each operation. If you want to be able to interrupt the doSomthing() operation itself, you'll have to provide a mechanism for that. One likely approach would be to use CancellationSource and CancellationToken, which provides a convenient way to express cancellation semantics.
Try using an async/await approach. It's quite easy!
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
private CancellationTokenSource _tokenSource;
private async void start_Click(object sender, EventArgs e)
{
if (_tokenSource != null)
return;
_tokenSource = new CancellationTokenSource();
var ct = _tokenSource.Token;
await Task.Factory.StartNew(() =>
{
for (; ; )
{
if (ct.IsCancellationRequested)
break;
doSomething();
}
}, ct);
_tokenSource = null;
}
private int _labelCounter;
private void doSomething()
{
// do something
Invoke((Action)(() =>
{
myLabel.Text = (++_labelCounter).ToString();
}));
}
private void stop_Click(object sender, EventArgs e)
{
if (_tokenSource == null)
return;
_tokenSource.Cancel();
}
}
try this :
bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}
and in the click event
set
stop=true;
OK so I've have a problem looking like this.
public Class A{
public A(){
progressBar.Style = ProgressBarStyle.Marquee;
progressBar.MarqueeAnimationSpeed = 0;
}
public void DoSomething(){
if(checkpasses){
progressBar.MarqueeAnimationSpeed = 100;
//Do something here...
progressBar.MarqueeAnimationSpeed = 0;
}
else
//Do nothing...
}
}
The problem is that my progressbar wont start moving at all. First I figured that it wont create a new thread by itself (which I find wired) so I tried creating a thread but still the same result. Nothing happens. Is it something I've forgotten?
Call
Application.EnableVisualStyles();
at the very beginning of your application.
Your "do something here" code is going to block the UI thread so you will not see the progress bar update until after the DoSomething method completes. At that time you are setting the animation speed back to 0.
Try putting your "do something here" code on a separate thread. When that thread completes set the animation speed back to 0.
Something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.MarqueeAnimationSpeed = 0;
progressBar1.Style = ProgressBarStyle.Blocks;
progressBar1.Value = progressBar1.Minimum;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
DoSomething();
}
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 100;
backgroundWorker1.RunWorkerAsync();
}
private void DoSomething()
{
Thread.Sleep(2000);
}
}
I am not sure if this is the best solution, but I have it this way:
//this is the action item (button click)
private void importSFNFReportButton_Click(object sender, EventArgs e)
{ //I run
backgroundWorker6Progress.RunWorkerAsync(); //this is how I start the progress bar 'movement'
bgwImportSF.RunWorkerAsync(); //this is another task that is lauchned after the progress bar is initiated
}
This is actual background worker
private void backgroundWorker6Progress_DoWork(object sender, DoWorkEventArgs e)
{
bool cont = true;
while (cont)
{
PauseForMilliSeconds(100);
updateProgressbar1(false);
if (noTasksExistCheck())
{
updateProgressbar1(true);
cont = false;
}
}
}
this is a delegate- I call it to auto-increase the progress bar indicator
delegate void updateProgressBarStatus(bool done);
private void updateProgressbar1(bool done)
{
if (progressBar1.InvokeRequired)
{
updateProgressBarStatus del = new updateProgressBarStatus(updateProgressbar1);
progressBar1.Invoke(del, new object[] { done });
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
progressBar1.PerformStep();
if (done == true)
{
progressBar1.Value = progressBar1.Minimum;
}
}
}
I control it via the function that has to check a global varibale
noTasksExistCheck()
This is the timer pause
public static DateTime PauseForMilliSeconds(int MilliSecondsToPauseFor)
{
System.DateTime ThisMoment = System.DateTime.Now;
System.TimeSpan duration = new System.TimeSpan(0, 0, 0, 0, MilliSecondsToPauseFor);
System.DateTime AfterWards = ThisMoment.Add(duration);
while (AfterWards >= ThisMoment)
{
System.Windows.Forms.Application.DoEvents();
ThisMoment = System.DateTime.Now;
}
return System.DateTime.Now;
}
Just to complement a bit more, the solution suggested by Dave will only work if Konstantin's suggested code exists. Otherwise, one should think of manually increasing the progressbar.value in a loop by the following code within the DoWork:
BeginInvoke(new MethodInvoker( () => progressBarSave.Value += progressBarSave.Step));