Question

In my wp7.5 app, i have a block where i need to show a stopclock (say 30,29,28...1,0). I tried various implementations to achieve this using DispatchTimer and Timer classes, but none of them solved my problem.

Approach 1: Here is the snippet i used for DispatchTimer,

DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 1); // 1 second
dt.Tick += new EventHandler(dt_Tick);

for(int count=0;count<30;count++)
    dt.Start();

void dt_Tick(object sender, EventArgs e)
{
   // my UI control update here
}

in my Tick event implementation i am updating a UI control with time counter. I read some questions here on the same topic, where dispatcher tick never fires in some scenarios because of UI thread. It happened same to me, tick event never fired.

Approach 2: I tried using System.Threading.Timer class,

Timer timer = new Timer(TimerProc);

for(int count=0;count<30;count++)
    timer.Change(1000, 0);

void TimerProc(object sender)
{
   // my UI control update here
}

None my approaches worked. I might have asking a repeated question, can anyone point me where I am doing incorrectly in the code?

Was it helpful?

Solution

The DispatcherTimer after the Start() method called keep fires the Tick event when the interval elapses until when you call Stop() on it. So you don't need to call Start() 30 times but you have to maintain a counter and in the Tick event handler stop the timer after 30 ticks:

private int counter;

private void Start_Click(object sender, RoutedEventArgs e)
{
    counter = 30;
    DispatcherTimer dt = new DispatcherTimer();
    dt.Interval = new TimeSpan(0, 0, 1); // 1 second
    dt.Tick += new EventHandler(dt_Tick);
    dt.Start();
}

void dt_Tick(object sender, EventArgs e)
{
    if (counter >= 0)
    {
        timeText.Text = counter.ToString();
        counter--;
    }
    else
        ((DispatcherTimer)sender).Stop();
}

Edit:
If the precision of the DispatcherTimer is not enough you can use System.Threading.Timer instead, but in this case you have call Dispatcher.BeginInvoke in the tick event handler to enable access for objects on the UI thread:

private int counter;

private void Start_Click(object sender, RoutedEventArgs e)
{
    counter = 30;
    timeText.Text = counter.ToString();
    Timer dt = new Timer(dt_Tick);
    dt.Change(1000 /* delay to start the timer */, 1000 /* period time */);
}

private void dt_Tick(object sender)
{
    if (counter > 0)
    {
        Dispatcher.BeginInvoke(() => timeText.Text = counter.ToString());
        counter--;
    }
    else
        ((Timer) sender).Dispose();
}

OTHER TIPS

The Timer tick method musn't be the one to keep track of the time. Why? For two reasons.

1/ The interval between each Tick of the DispatcherTimer isn't precise, and depends on what you do with the UI. It can be 1 second, like it can be a bit more.

2/ The next Tick is triggered one second after the previous tick has ended. Since the update code inside the tick takes some time, it will automatically lag behind. For instance, if the update code takes 0.1 second, the tick is triggered after 1 second, the update is done, and the next tick comes 1 second after the end of the update, so 2.1 second after the start of the timer!

Therefore, you should store the time when you started the Timer, and compute the elapsed time at each tick:

    protected DispatcherTimer Timer { get; set; }

    protected DateTime TimerStartTime { get; set; }

    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        this.Timer = new DispatcherTimer();
        this.Timer.Interval = TimeSpan.FromSeconds(1);
        this.Timer.Tick += this.Timer_Tick;
        this.TimerStartTime = DateTime.Now;
        this.Timer.Start();
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
        int elapsedSeconds = (int)DateTime.Now.Subtract(this.TimerStartTime).TotalSeconds;

        this.TextTimer.Text = (30 - elapsedSeconds).ToString();
    }

This way, even if the time between each tick isn't precise, the timer won't indicate a false time. You can even reduce the timer interval to 500 ms or less to have a more precise timer, without changing the Tick method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top