I am trying to make a stop button for this loop but it runs indefinite, nothing happens when i click button 2
bool dowhile = false;
private void button1_Click(object sender, EventArgs e)
{
do
{
for (int i = listbox1.Items.Count - 1; i >= 0; i--)
{
string textstring = listbox1.Items[i].ToString();
richTextBox1.AppendText("" + textstring + ": Done\n");
Thread.Sleep(1000);
}
} while (!dowhile);
}
private void button2_Click(object sender, EventArgs e)
{
this.dowhile = true;
}
where do i go wrong ?
sry for the "lvlchanger" typo, code is ok now, nothing missing
i'm also looking for a not-so-long fix on this :))
The system can't process anything from the message queue (i.e. button clicks, repaints, etc) until your button1_Click completes - which it never will. This is exactly what causes all those "{blah} is not responding" messages - code that doesn't respond to the message queue promptly.
Basically, don't do that. A hacky fix would be some DoEvents(), but NO! do it properly; basically, handle the event from button2_Click instead. Perhaps you should run the refreshes from a timer tick?
Thread.Sleep is almost always the incorrect approach; when you find yourself wanting to use something like it or Application.DoEvents in your code, it's time to take a long step back and think about what you're really doing and why it isn't working. A new design should be in your future.
The real problem here is that you're doing work on the UI thread, which blocks the user from interacting with your application. Your click on Button2 is getting recognized and processed after the loop has already finished processing, at which point it does absolutely nothing.
This is because threads can only complete one task at a time. And, in fact, Thread.Sleep(100) only makes this worse, because it forces the thread to spin and do nothing for 100 milliseconds. That's 100 more milliseconds it will take for the loop to complete for absolutely no gain.
The correct solution to this (common) problem is to spin the loop off onto a separate thread, using something like the BackgroundWorker class. The MSDN entry has a very good example of its use, including your specific use case: allowing the user to cancel a long-running background task.
private void button1_Click(object sender, EventArgs e)
{
bool dowhile = false;private void button1_Click(object sender, EventArgs e){
do
{
for (int i = listbox1.Items.Count - 1; i >= 0; i--)
{
string textstring =listbox1.Items[i].ToString();
richTextBox1.AppendText("" + textstring + ":` `Done\n");
Thread.Sleep(1000);
}
} while (!lvlchanger && dowhile == false);
}
private void button2_Click(object sender, EventArgs e)
{
this.dowhile = true;
}
Add an Application.DoEvents() to the loop to allow the application to process events from other sources.
/EDIT
This should work, but...
"This is almost never the correct answer" - Cody Gray
Related
Having the Code Below in Windows forms.
private bool test = false;
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
test = !test ;
textBox2.Text = test.ToString(); // Each time i click text box switches to true and false
for (int i = 0; i < 1000000; i++)
{
textBox1.Text = i.ToString();
}
});
textBox2.Text = "Done"; // This will never happen until the task is done
}
If i Click button the textbox text Changes from 0 to 1000000.
Since i use async/await. the form will not freeze and I can see the Textbox Counting from 0 to 1000000.
But the problem is if i click the button again another thread spawn and textbox value changes by two threads. and you can see two counters from two threads changing textbox value.
if you click again you get 3 threads, 4 threads etc....Also textbox2 changes to true, false, true ....
This was just a test for me to see how actually async await works.
But i think im using it wrong. im afraid if i use async in my projects and end up to this situation.
How can i stop Threads spawning from single async method.
Currently I think each time i press the button New async Method spawns.
Here is What i see.
There's some confusion in the comments about what's happening. Especially since the posted code shouldn't actually work (it's not thread-safe).
The simplest way to reproduce this (drop a Button and a Label on a Form):
private async void button1_Click(object sender, EventArgs e) // add the async
{
for (int i = 0; i < 1000; i++)
{
label1.Text = "" + i;
await Task.Delay(100);
}
}
You can make this run multiple loops at once, you can increase the Delay to see that better. Note that there are no extra Threads involved, it all runs on the main GUI thread.
The keys here are async and await, they make the compiler turn a call to this method into a state machine and that way it can interleave the execution of multiple loops at once. Think of it as a method that can be paused at the await call and be resumed later. All on the same thread.
More importantly, it interleaves the loop(s) with the main thread so that that can continue to handle input messages and update the screen.
That was the 'why' part.
The how to solve it part depends on what you actually want, the now deleted answer from #CSharpie shows the basic pattern: use a boolean field as a guard, or disable & enable the Button.
Disable the button after pressed and and enable it when it finishes.
Im building a little app which has a long loading time.
I want to display this loading time in a progressbar to see how long i have to wait till the programm is loaded.
I hope you understand what i want..
I tried the backgroundworker already but dont understand how to use it, in every example they use in the DoWork Event a simple
for (int i = 0; i < 100; i++)
{
//method etc here
backgroundWorker.ReportProgress(i);
}
But in my eyes this is senseless for me because this only repeats my method...
Thank you in advance!
EDIT:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Aktie dataAktie = new Aktie(aktien_name);
for (int i = 0; i < 100; i++)
{
dataAktie.ReadFromDatabase();
dataAktie.FetchData();
backgroundWorker.ReportProgress(i);
}
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Controls that have to be filled
}
But this Controls dont get data im veryyyyy confused
The following code example demonstrates the use of the ReportProgress method to report the progress of an asynchronous operation to the user.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This method will run on a thread other than the UI thread.
// Be sure not to manipulate any Windows Forms controls created
// on the UI thread from this method.
backgroundWorker.ReportProgress(0, "Working...");
Decimal lastlast = 0;
Decimal last = 1;
Decimal current;
if (requestedCount >= 1)
{ AppendNumber(0); }
if (requestedCount >= 2)
{ AppendNumber(1); }
for (int i = 2; i < requestedCount; ++i)
{
// Calculate the number.
checked { current = lastlast + last; }
// Introduce some delay to simulate a more complicated calculation.
System.Threading.Thread.Sleep(100);
AppendNumber(current);
backgroundWorker.ReportProgress((100 * i) / requestedCount, "Working...");
// Get ready for the next iteration.
lastlast = last;
last = current;
}
backgroundWorker.ReportProgress(100, "Complete!");
}
** http://msdn.microsoft.com/en-us/library/a3zbdb1t%28v=vs.110%29.aspx
A BackgroundWorker and it's ReportProgress method are no magic wanda that simply shows you any progress you want, you actually have to change your code to do so.
The DoWork event handler should contain the code you want to execute in the background. Ideally this is something for progress can be measured easily. For example if you have to process 10 items then after each item you could say I'm now 10% further done. That's why the example code contains a for loop.
Your code only contains two method calls, ReadFromDatabase and FetchData. So you could simply do
dataAktie.ReadFromDatabase();
backgroundWorker.ReportProgress(50); // 50% done
dataAktie.FetchData();
backgroundWorker.ReportProgress(100); // 100% done
Obviously that not really perfect. The only way to have more accurate progress is to change the ReadFromDatabase and FetchData methods, for example let them take the BackgroundWorker object as a parameter so that they can also report progress, or provide a callback for that.
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);
}
}
}
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.
So I have two event handlers button1_Click() and button2_Click()
In button1_Click() I have something running like this:
toGet = textbox1.Text;
got = 0;
while (got <= toGet)
{
//DoStuff
}
But button2_Click is supposed to be a stop button, and stop button1 early.
How do I go about this?
Thanks for the help. I saw this article here about it, but couldn't get it to work.
Windows.Forms answer
The least sophisticated method is this:
private bool m_stop;
private void button1_Click (object s, EventArgs ea)
{
try
{
// Don't forget to disable all controls except the ones you want a user to be able to click while your method executes.
toGet = textbox1.Text;
got = 0;
while (got <= toGet)
{
Application.DoEvents ();
// DoEvents lets other events fire. When they are done, resume.
if (m_stop)
break;
//DoStuff
}
finally
{
// Enable the controls you disabled before.
}
}
private void button2_Click (object s, EventArgs ea)
{
m_stop = true;
}
It has the distinct advantage of letting you execute button1_Click on the UI thread, still lets the UI respond to your stop button.
It has a disadvantage that you must protect against reentrancy. What happens if they click your button1 while button1_click is already executing!?!?
Edit: Another way I have used is to use a Timer instead of a loop. Then, the stop method just stops the timer.
As much as I understood, correct me if I'm wrong, you're on single thread.
Wired, but you can check for single boolean value inside the your While loop, just as post suggested.
May be to make life easier (may be this is what "couldn't get it to work" means) is inside loop call
1) Windows Forms: Application.DoEvents()
2) WPF (little bit more tricky) : DoEvents in WPF
This to make breathe system.
You need to start the process inside the button1 in new thread, and when you press the button2 flag a local variable to false to stop the loop. like:
using System.Threading;
private volatile bool _requestStop = false;
private readonly object _oneExecuteLocker = new object();
private void OnButton1Click(ojbect sender, EventArgs e)
{
new Thread(() =>
{
if (Monitor.TryEnter(_oneExecuteLocker))
{//if we are here that is means the code is not already running..
try
{
while (!_requestStop)
{
//DoStuff
}
}
finally
{
Monitor.Exit(_oneExecuteLocker);
}
}
}){ IsBackground = true }.Start();
}
private void OnButton2Click(object sender, EventArgs e)
{
_requestStop = true;
}
Notes:
When ever you want to update a UI control inside the newly created thread you should use contorl.Invoke(/*the code get/set or call method in the UI*/).
The Monitro.Enter is just to be sure that your code will not executed multiple time per click if it already running.