I'm new learner in C# and i have a simple problem.
I do classic loop of 15 iterations with a Threat.Sleep of 3 secondes in each ones and inside this loop at each iteration i'm addind text to my RichTextBox.
BUT the text only appear at the end of the 15 iterations of 3 secondes ! :(
I want the text to be added at each iteration in the loop :) Is there a way to do that ?
Thank you for helping :)
AMIGA RULEZ
I tested this code and it worked.
When i press the button, the RTB adds the word "hello" each 3 seconds.
private async void button1_Click(object sender, EventArgs e)
{
for(int i=0; i<16; i++)
{
richTextBox1.Text += "hello";
await Task.Delay(3000);
}
}
Thread.Sleep will block the current thread. When a thread is blocked it cannot do anything else. So if it is the UI thread it cannot re-render the UI until the thread is unblocked. Because of this it is recommended to never block the UI thread.
A workaround is to use Task.Delay and async/await instead. Internally this will cause your code to be rewritten to a state machine. In principle transforming it to something like the following pseudo code
int i = 0;
timer = new Timer(TimerCallback, period: 3s);
timer.Start();
...
public void TimerCallback(){
if(i >= 16){
timer.Stop();
return;
}
richTextBox1.Text += "hello";
}
Related
I need to understand how can I create delays between a set of commands. My background is with C (DOS) and now reviving the concepts using C# in Visual Studio 2015. This is the code I am struggling with:
using System.Threading;
private void button1_Click(object sender, EventArgs e) // Button
{
int i;
for (i = 0; i < 10; i++)
{
textBox1.BackColor = Color.Red;
Thread.Sleep(100);
textBox1.BackColor = Color.Yellow;
Thread.Sleep(100);
}
}
I was expecting the background color of the textbox will change alternatively 10 times but I could see only yellow color after the loop finishes. If I increase delay I do notice that program takes time to finish. I went through some related articles but couldn't get the point. Any help will be appreciated. Thanks
Use an async method to create a delay using the built-in Task.Delay method. This will cause execution to be paused and then resumed after the specified time without blocking the current thread.
async Task UseDelay()
{
await Task.Delay(1000); // wait for 1 second
}
In your specific case
async void button1_Click(object sender, EventArgs e)
{
for (var i = 0; i < 10; i++)
{
textBox1.BackColor = Color.Red;
await Task.Delay(100);
textBox1.BackColor = Color.Yellow;
await Task.Delay(100);
}
}
The problem isn't with the delaying, it's with the threading model of UI applications. In a UI application, any event handlers happen on the "UI thread". While that code is running, the UI is essentially frozen, so nothing you do there matters.
That means if you "Sleep" in there, the entire application will lock up and be unresponsive until it returns.
But you can make your event handlers "async void" instead of just "void" if they need to do long running work. Then if you await Task.Delay(someTime) instead of Thread.Sleep(someTime), it will release the UI thread while that's happening. And you should see what you expect without the UI locking up.
Also, that time is in milliseconds, meaning wait "one tenth of a second". Which is really, really fast. You might want to slow it down a bit to get the effect you want.
I need to create a script such as chronometer.
I write a code like following;
for(int i=0;i<50;i++)
{
textBox.Text = i.Tostring();
Task.Delay(100).Wait();
}
The expected output is like a chronometer ; an increasing text by 1 up to 49 started from 0 at textbox.
But I get only 49 after a 49*100 miliseconds pause later.
How can I solve this ?
The event or method running this piece of code needs to be asynchronous. This is in order for the UI to be responsive:
private async void btnDoWork_Click(object sender, EventArgs e)
{
for(int i=0;i<50;i++)
{
textBox.Text = i.Tostring();
await Task.Delay(100);
}
}
Otherwise, you'll be blocking the UI Thread and you will not be able to see the Text Box changing. You'll only see the last change which is 49 in your case.
So I have a very basic Windows Application where I want to count from 1 to 10000
and show the numbers in label:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
for (int i = 0; i < 10000; i++)
{
BeginInvoke((MethodInvoker)delegate ()
{
label3.Text = i.ToString();
});
}
});
thread.Start();
}
The problem is that the label text doesn't update and shows only the last loop counter i.e. 9999. Is BeginInvoke called on UI thread? Why does not the label get updated correctly?
Thanks.
Because BeginInvoke is an asynchronous call, you're sending too many updates to the text box for it to update fast enough, by the time the text box has got around to drawing, it's already counted up to 10000!
You can synchronously update the text, that is, the calling loop will halt until the text box has updated and finished, use Invoke instead of BeginInvoke.
I try to experiment with Thread.Sleep(). I created basic Windows Forms application with one button.
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
public void DoStuff()
{
//DoStuff
}
When I click my button the DoStuff method works fine, but the GUI freezes and nothing happens. Can someone explain me why?
Thread.Sleep just sleeps the current thread (i.e. stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread. If you put the Sleep in DoStuff you wouldn't experience the block as you'd be on a separate thread although you wouldn't be able to update button1. Depending on the version of .NET you're using consider using the Task Parallel Library, something like this:
private TaskScheduler _uiScheduler;
public Form1()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
// Create a task on a new thread.
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
// Create a new task on the UI thread to update the button
Task.Factory.StartNew(() =>
{ button1.Text += "."; }, CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
}
});
}
To keep the UI active, you need for the main UI thread to service its message pump. It can only do that when it is not handling UI events. In your case the function
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
does not return for around 100000*500 milliseconds. While this event handler is executing, the UI thread is busy. It is executing this event handler. As such it is not able to service the message pump. Hence your application's UI freezes.
For that you better use a Timer but if you want your current code to work you need to add Application.DoEvents(); after you update the button.Label += "."
If you're new to multithreading, I strongly encourage you to look at the Task Parallel Library (TPL). It simplifies threading, and gives you tools to help guarantee callback (continuation) threads occur on the UI thread.
The TPL is in the System.Threading.Tasks namespace.
Update: just seen your comment about .Net v2. TPL was introduced in .NET v3.5 or possibly as late as v4.
EDIT: After programming for a few more years, I now know how terrible of a practice this is. DO NOT DO ANYTHING I SUGGESTED BELOW. It's all crap. A more proper solution would be doing all of your intensive methods async all together. Regardless, don't do what I mention below.
All The methods above do work however, I do recommend just using an async void.
Sleep() just pauses the current thread for int amount of milliseconds, and if your whole program runs off of 1 thread, it'll pause the whole program. Don't quote me on this, I do believe that async creates a new thread specifically for that function.
Below I've included a better sleep function.
To call the function asleep(milliseconds),
replace "milliseconds" with how many milliseconds you wish to sleep for.
Function Code:
public async void asleep(int time){
await Task.Delay(time)
}
Re-arrange code as following
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
}
public void DoStuff()
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
//Invoke goes here
}
}
Now you run your WORK in a separate thread and release your UI thread for usual work (Drawing related or other work)
NOTE - Now you will need Invoke methods to change Button text , else you will get warning for "Cross-thread operation not valid"
More on Invokes - How to update the GUI from another thread in C#?
I can't seem to change the text on any object - I've tried both labels and buttons so far. Why doesn't this work?
void Button1Click(object sender, EventArgs e)
{
for(int i = 60; i >=1; i--){
Thread.Sleep(1000);
i -= 1;
label1.Text = i.ToString();
}
}
It doesn't work because you are using a busy loop to update the text.
This code runs in the main thread, so it's busy setting the Text property for a whole minute, and can't update the user interface.
You would use a timer to update the text, so that the main thread regains the control in between changes.
Try taking out the Thread.Sleep() command to see if the label is updated. You may find that the UI thread exits the function even though you requested it to sleep.
What you can do is use a Timer control, and set the interval to 1000 (1 second). Then you can set the label1.Text to a counter or static field value (or hidden field).
As long as you stay in the Button1Click the UI thread seams to be sleeping. Delete the Thread.Sleep and you will see that the text is shown in the labels.
put Application.DoEvents(); in your code after last line, then it will work for sure.
for (int i = 60; i >= 1; i--)
{
Thread.Sleep(1000);
i -= 1;
label1.Text = i.ToString();
Application.DoEvents();
}
First of all you have to learn How to: Make Thread-Safe Calls to Windows Forms Controls.
You need to create a thread and use Invoke delegate.
Thread th = new Thread(test);
th.Start(); //start the thread
This method will update the lable.text
void test()
{
for (int i = 60; i >= 1; i--)
{
Thread.Sleep(1000);
if (label1.InvokeRequired)
{
label1.Invoke(new Action(() => {
label1.Text = i.ToString();
}));
}
}
}