Timer and refreshing the UI

  • Thread starter Thread starter Daniel P.
  • Start date Start date
D

Daniel P.

I'm trying to set a timer that gets called every 3 seconds so I can update a
field in the UI with the time elapsed since the process started.

What am I doing wrong that timerDF_Tick does not get called?



private System.Windows.Forms.Timer timerDF;
this.timerDF = new System.Windows.Forms.Timer(this.components);

this.timerDF.Interval = 3000;
this.timerDF.Tick += new System.EventHandler(this.timerDF_Tick);

timerDF.Enabled = true;
timerDF.Start();


// here I call a stored procedure that takes about 10 minutes to run
......

private void timerDF_Tick(object sender, System.EventArgs e)
{
DateTime dt2 = DateTime.Now;
TimeSpan diff = dt2 - dt1 ;

textProcessTime.Text = string.Format( "{0}:{1}:{2}",
diff.Hours.ToString("00"),
diff.Minutes.ToString("00"),
diff.Seconds.ToString("00") );

textProcessTime.Update();

this.Update();
}
 
Where are you putting the this.timerDF.Tick += new
System.EventHandler(this.timerDF_Tick) statement? I would put it in your
Load event, or somewhere near the init of the win form.

But, It doesn't look like you are doing anything wrong (although I would use
System.Timers.Timer as opposed to System.Windows.Forms.Timer). Also, you
shouldn't need to call textProcessTime.Update() or this.Update().

Have you tried placing a breakpoint in the timerDF_Tick event to see if it
is not firing?

--
HTH

Kyril Magnos
"I'm not a developer anymore, I'm a software engineer now!" :-)

| I'm trying to set a timer that gets called every 3 seconds so I can update
a
| field in the UI with the time elapsed since the process started.
|
| What am I doing wrong that timerDF_Tick does not get called?
|
|
|
| private System.Windows.Forms.Timer timerDF;
| this.timerDF = new System.Windows.Forms.Timer(this.components);
|
| this.timerDF.Interval = 3000;
| this.timerDF.Tick += new System.EventHandler(this.timerDF_Tick);
|
| timerDF.Enabled = true;
| timerDF.Start();
|
|
| // here I call a stored procedure that takes about 10 minutes to run
| .....
|
| private void timerDF_Tick(object sender, System.EventArgs e)
| {
| DateTime dt2 = DateTime.Now;
| TimeSpan diff = dt2 - dt1 ;
|
| textProcessTime.Text = string.Format( "{0}:{1}:{2}",
| diff.Hours.ToString("00"),
| diff.Minutes.ToString("00"),
| diff.Seconds.ToString("00") );
|
| textProcessTime.Update();
|
| this.Update();
| }
|
|
 
Kyril Magnos said:
Where are you putting the this.timerDF.Tick += new
System.EventHandler(this.timerDF_Tick) statement? I would put it in your
Load event, or somewhere near the init of the win form.

It's in InitializeComponent.
But, It doesn't look like you are doing anything wrong (although I would use
System.Timers.Timer as opposed to System.Windows.Forms.Timer). Also, you
shouldn't need to call textProcessTime.Update() or this.Update().

Have you tried placing a breakpoint in the timerDF_Tick event to see if it
is not firing?

I did put a breakpoing and it is not firing.
 
Hi,
inline

Daniel P. said:
I'm trying to set a timer that gets called every 3 seconds so I can update a
field in the UI with the time elapsed since the process started.

What am I doing wrong that timerDF_Tick does not get called?

The timer event can only fire if the message loop is running and it's
blocked because the 10minute during sp is executed inside an event handler
(or inside a function that's called from an event handler).

There are other timers that can fire while the message loop is blocked, but
it won't do you any good because then you would have to marshal from a
system thread to the UI thread, which also doesn't work when the message
loop is blocked.

What you need to do : call this 10minute stored procedure from a
workerthread. Then the message-loop won't be blocked and timer event's will
fire.

eg:

public void SomeMethod()
{
private System.Windows.Forms.Timer timerDF;
this.timerDF = new System.Windows.Forms.Timer(this.components);

this.timerDF.Interval = 3000;
this.timerDF.Tick += new System.EventHandler(this.timerDF_Tick);

timerDF.Enabled = true;
timerDF.Start();
Thread tr = new Thread(new ThreadStart(RunSP));
tr.Start();
}

public void RunSP()
{
// here you should call a stored procedure that takes about 10
minutes to run,
// so it doesn't block the UI thread (message loop)
}


HTH,
greetings
 
Thanks! That did it. The worker thread is doing its job while the main
thread displays the updated timer.
Is there a more efficient way to wait for the worker thread then using Sleep
as below?
I was thinking to use a callback to the main thread but that creates strong
coupling.

Thread t = new Thread(new ThreadStart( myObj.ThreadProc ) );
t.Start();

while( true == t.IsAlive )
{
Thread.Sleep(1000);
UpdateTimeElapsed();
}
 
Daniel P. said:
Thanks! That did it. The worker thread is doing its job while the main
thread displays the updated timer.
Is there a more efficient way to wait for the worker thread then using Sleep
as below?
I was thinking to use a callback to the main thread but that creates strong
coupling.

Thread t = new Thread(new ThreadStart( myObj.ThreadProc ) );
t.Start();

while( true == t.IsAlive )
{
Thread.Sleep(1000);
UpdateTimeElapsed();
}

If you wait for the thread to end, then you don't gain much by using a
worker thread. You're are still blocking the message loop this way,
remember that's it's no good to do lenghty operations inside eventhandlers,
they make the UI non-responsive.

Start the timer and thread, don't wait for the thread to end, but let the
thread fire an event when it's done, and from that event you could stop the
timer.


HTH,
greetings
 
A while loop with a Sleep seems to work fine.

If I use an event and the ClickButton method ends after starting the thread
then how can I keep the UI disabled so the user cannot click the button
again or launch other reports?
 
Hi,

Daniel P. said:
A while loop with a Sleep seems to work fine.

Can you minimize, maximize or move your form ?
If I use an event and the ClickButton method ends after starting the thread
then how can I keep the UI disabled so the user cannot click the button
again or launch other reports?

Sure, using threads may cause re-entrance problems, but that's easely
solved. Disable the button after the user presses it:
command1.Enabled = false;

Then re-enable it after the finish-event fired:
command1.Enabled = true;

I know threads can cause new problems but you should really not make long
loops in UI-event-handlers. There is always one thread (the main thread)
and for windows applications also called the UI thread. This thread does
nothing else then asking the OS if there is a message (mouse_click,
key_pressed, etc) and then runs the appropriote event (this event loop is
inside Application.Run). So only one event can happen at any time. If you
do lenghty operations inside an event, no other events have a chance to
fire, you block the UI.



HTH,
greetings
 
BMermuys said:
Hi,



Can you minimize, maximize or move your form ?

No, but it does not look like an issue for the user.
Sure, using threads may cause re-entrance problems, but that's easely
solved. Disable the button after the user presses it:
command1.Enabled = false;

Then re-enable it after the finish-event fired:
command1.Enabled = true;

Yes, I was thinking to do that but the user can close the Form or close the
app or launch another report, etc and this is not good.
I know threads can cause new problems but you should really not make long
loops in UI-event-handlers. There is always one thread (the main thread)
and for windows applications also called the UI thread. This thread does
nothing else then asking the OS if there is a message (mouse_click,
key_pressed, etc) and then runs the appropriote event (this event loop is
inside Application.Run). So only one event can happen at any time. If you
do lenghty operations inside an event, no other events have a chance to
fire, you block the UI.

The while loop with a Sleep does not seem to take a lot of CPU, the worker
thread does its job fine and for the time being I'll leave it the way it is
but I am still interested in a better solution for a future release.
 
Back
Top