Can I use a timer to update a label every x milliseconds - c#

This is my code:
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.ElapsedMilliseconds < 3000) {
label1.Text = Convert.ToString( timer.ElapsedMilliseconds );
}
timer.Stop();
My intetion was to update the label's text in real time, so if timer.ElapsedMilliseconds == 1350, then label1.Text = 1350. How can I do this? Thanks in advance!

You better to use System.Windows.Forms.Timer for this, and not Stopwatch()
Even if that timer is less accurate then StopWatch(..) it gives you a good control.
Just example sniplet:
myTimer.Tick += new EventHandler(TimerEventProcessor);
myTimer.Interval = 1350;
myTimer.Start();
private void TimerEventProcessor(...){
label1.Text = "...";
}

You cannot update the UI in a tight loop like that, because while the UI thread is running that code, it isn't responding to paint events. You can do nasty things like "DoEvents()", but please don't... it would be better to just have a Timer and update the UI periodically when the timer event fires; every 50ms would be the absolute fastest I'd go, personally.

Is this a WinForms app?
The problem is that while your loop runs, it does not give any other tasks (like updating the GUI) any possibility to get done, so the GUI will update the the entire loop is complete.
You can add a quick and "dirty" solution here (if WinForms). Modify your loop like this:
while (timer.ElapsedMilliseconds < 3000) {
label1.Text = Convert.ToString( timer.ElapsedMilliseconds );
Application.DoEvents();
}
Now the label should update in between the loop runs.

If you want it update every second you could use the modulus operator in your while loop:
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.ElapsedMilliseconds < 3000) {
if (timer.ElapsedMilliseconds % 1000 == 0)
{
label1.Text = timer.ElapsedMilliseconds.ToString();
}
}
timer.Stop();
The modulus operator gives the remainder of a division operation, if the milliseconds are a multiple of 1,000 it will return 0.
I'd probably look into using Timers. You do a lot of spinning using the above technique, that may cause your UI to be unresponsive.

Related

To block the main thread while a timer is running C#

I am trying to do something with a timer in a loop and after the timer finishes its work, app starts another turn in the loop. But because I don't know how to ask the main thread stop running while timer is running, the main thread goes to the next turn immediately.
I did something about lock.
This is the loop
for (int i = 0; i < step; i++)
{
Monitor.Enter(locker);
//start timer
}
then the code inside of the timer
t_tick = (senders, args) =>
{
if (condition)
{
//do something
}
else
{
//do something
Monitor.Exit(AirplaneManager.locker);
t.Stop();
}
};
t.Tick += t_tick;
t.Interval = 30;
t.Start();
But this gives me an exception while the code runs into the monitor in timer : Object synchronization method was called from an unsynchronized block of code.
Is there any solution? Or I can use other way to reach my goal?
Thanks!
If you want the current thread to block for a set timespan, you can just use Thread.Sleep(TimeSpan)
https://msdn.microsoft.com/en-us/library/274eh01d(v=vs.110).aspx
You don't need to use another thread.

Thread runs slow when Invoke UI-Element

i am programming a benchmark tool, that reads a bunch of variables from a local server in a thread.
int countReads = 1000;
Int64 count = 0;
for (int i = 0; i < countReads; i++)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
DateTime start = DateTime.Now;
session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);
DateTime stop = DateTime.Now;
Thread.CurrentThread.Priority = ThreadPriority.Normal;
TimeSpan delay = (stop - start);
double s = delay.TotalMilliseconds;
count += (Int64)s;
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
progressBar1.Value = i;
}));
}
double avg = (double)count / countReads;
Dispatcher.Invoke(DispatcherPriority.Input, new Action(() =>
{
listBox1.Items.Add(avg);
}));
I am calculating the timespan it took to proceed the read and getting the average timespan at the end.
DateTime start = DateTime.Now;
session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);
DateTime stop = DateTime.Now
if i run the code without updating the progressbar it took about 5ms average.
but if i run it with
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
progressBar1.Value = i;
}));
it takes about 10 ms average.
My question is, why is the timespan higher when using the progressbar?
i am just calculating the timespan for the read. Not including the progressbar update.
Is there any way to evacuate the ui-painting so that it doesn´t effect my read-timespan?
Thanks for your help.
Best regards
Stop using Invoke to transfer progress information to the UI thread. Publish the progress information to a shared data structure or variable and have the UI thread poll for it using a timer on a reasonable interval. I know it seems like we have all been brainwashed into thinking Invoke is the be-all method for doing worker-to-UI thread interactions, but for simple progress information it can be (and often is) the worst method.
A polling method using a timer on the UI thread offers the following benefits.
It breaks the tight coupling that Invoke imposes on both the UI and worker threads.
The UI thread gets to dictate when and how often it should update the progress information instead of the other way around. When you stop and think about it this is how it should be anyway.
You get more throughput on both the UI and worker threads.
I know this does not directly answer your question as to why session.Read appears to run slower. Try changing your strategy for updating progress information from a push model (via Invoke) to a pull model (via a timer). See if that makes a difference. Even if it does not I would still stick with the pull model for the reasons listed above.
Here is what MSDN says about Dispatcher.Invoke
Executes the specified delegate synchronously on the thread the Dispatcher is associated with.
So, basically, Dispatcher.Invoke blocks until the dispatcher thread as handled the request.
Try Dispatcher.BeginInvoke instead.
If current executing thread is associated with Dispatcher you are using - Invoke() will block this thread so in this case try out using Dispatcher.BeginInvoke() it will do the job asynchronously.
MSDN, Dispatcher.Invoke Method:
Invoke is a synchronous operation; therefore, control will not return
to the calling object until after the callback returns.
BTW, just of interest try out DispatcherPriority.Send
I came 9 years late to the party, but I think this is an even easier solution: Just wait until the progress bar value reaches a certain threshold before updating it. In my example, I refresh the toolbar every fifth of the maximum value.
private static int progressBarMaxValue = -1;
private static int progressBarChunkSize = -1;
public static void progressBarSetNotRealTimeValue(ProgressBar progressBar, int argNewValue)
{
if (progressBarMaxValue != -1)
{
if (argNewValue < progressBarChunkSize)
{
//Threshold not reached yet, discard the new value.
return;
}
else
{
//Allow the update, and set the next threshold higher.
progressBarChunkSize += progressBarChunkSize;
}
}
if (Thread.CurrentThread.IsBackground)
{
progressBar.BeginInvoke(new Action(() =>
{
if (progressBarMaxValue == -1)
{
progressBarMaxValue = progressBar.Maximum;
progressBarChunkSize = progressBar.Maximum / 5;
}
progressBar.Value = argNewValue;
}));
}
else
{
progressBar.Value = argNewValue;
}
}

Add a time interval between playing a wav file without locking up the GUI

I'm totally stumped on this one. I am creating a "click track" for an app that I'm making in Silverlight using C# (no VB.NET please). I have a slider on the page that I would like to represent the time interval between clicks. That being said, I would like my code to look something like this:
int i = 0;
while(i < 1)
{
using (var stream = TitleContainer.OpenStream("click.wav"))
{
var effect = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
effect.Play();
}
ClickInterval(clickSlider.Value);
i++;
}
ClickInterval() represents the method for the time interval between clicks. I'm not sure how to create this interval. A timer simply locks up the GUI and I'm not sure if threading is the way to go since this could create too many threads (which I'm not really sure that it would be a problem). Perhaps even the above code could be rewritten. Any ideas on this one folks? Any suggestions would be a big help!
Any Sleep in the same thread as the GUI will lock it up. You need to use another thread or a BackgroundWorker to play the clicks.
Your thread will contain code similar to above:
var stream = TitleContainer.OpenStream("click.wav")
var effect = SoundEffect.FromStream(stream);
while(true)
{
FrameworkDispatcher.Update();
effect.Play();
ClickInterval(clickSlider.Value);
}
As you can see, this will run continuously in the background until you stop the Thread. (If you have a 'stop clicking' button on the GUI, you can set a variable that will break out of the while loop.) I also brought out stream and effect so the thread wont keep creating and destroying them.
ClickInterval should basically call Thread.Sleep(clickSlider.Value) if the value being passed is in milliseconds.
My Original idea wouldn't work, but this might:
int i = 0;
string[] songs = new string[] { "click.wav" };
Timer timer = new Timer(){ Interval = clickSlider.Value * 1000 }; // 1000 = 1 second
EventHandler handler = (s,e) =>
{
timer.Stop();
PlayNext(songs, i++);
if(i < songs.Length)
timer.Start();
};
timer.Tick += handler;
handler(null,null); // starts timer and plays first song.
...
void PlayNext(string[] songs, int index)
{
using (var stream = TitleContainer.OpenStream(songs[index]))
{
var effect = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
effect.Play();
}
}

Program hangs on For loop when updating label

I have this code in a winform app:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Thread.Sleep(100);
}
I expected to see the label progress from 1 to 20 but instead it just hangs while the for loop is running and then displays a 20, i.e. I don't see 1-19.
Why is this and is there a way to update the label text quickly similar to the milliseconds on an analogue clock (I'm not making a clock, just an example.)
Thanks
*EDIT: This also happens if I have a button with an event that increments the number without Thread.Sleep but I have a beginInvoke to play a wav file *
You are sleeping the gui thread, so before the gui gets to update its slept again.
Try using a timer with a tick of 100ms
Call Application.DoEvents(); before sleep, that will process all messages in the queue:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Application.DoEvents();
Thread.Sleep(100);
}
The application hangs and you only see 20 simply because this would appear to be executing on the UI thread and you're Sleeping it, and therefore choking the window message loop. However, even if that wasn't the case, you'd most likely only ever see 20 anyway as the loop would execute so fast.
Use a System.Threading.Timer to execute every x milliseconds and update your label by invoking the appropriate method on the UI thread.
The following post demonstrates how to implement what you might want (albeit in the WPF environment, but the principle is the same - use ISynchronizeInvoke for WinForms as opposed to the Dispatcher shown in WPF here):
Timer callback closes WPF app (DispatcherTimer works..)
Use Timer control
int i=0;
private void timer_Tick(object sender, EventArgs e)
{
if(i <=20)
{
lbl.Text = i.ToString();
}
i++;
}
Set TimerInterval according to your need (Interval of 1000 = 1 second)

Why doesn't Win Forms application update label immediately?

I am doing some experimenting with threads, and made a 'control' method to compare against where all the processing happens in the UI thread. It should run a method, which will update a label at the end. This method runs four times, but the labels are not updated until all 4 have completed. I expected one label to get updated about every 2 seconds. Here's the code:
private void button1_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
UIThreadMethod(lblOne);
UIThreadMethod(lblTwo);
UIThreadMethod(lblThree);
UIThreadMethod(lblFour);
watch.Stop();
lblTotal.Text = "Total Time (ms): " + watch.ElapsedMilliseconds.ToString();
}
private void UIThreadMethod(Label label)
{
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
}
watch.Stop();
// this doesn't set text right away
label.Text = "Done, Time taken (ms): " + watch.ElapsedMilliseconds;
}
Maybe I'm just missing something basic, but I'm stuck. Any ideas? Thanks.
Your UI thread is a single thread, not two threads. To get your UI to be responsive, you either have to put the work on another thread (generally with a BackgroundWorker), or tell the UI to repaint itself in the UI thread.
I always have to experiment when I do things like this, but the Control.Refresh method should do it:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.refresh.aspx
You're blocking the UI thread by performing the sleep operations, which causes no redraws to happen. If you would sleep on another thread and Invoke the Label updates on the UI thread, then it should work as expected.

Categories

Resources