WPF: Timers
- by Ilya Verbitskiy
I believe, once your WPF application will need to execute something periodically, and today I would like to discuss how to do that. There are two possible solutions. You can use classical System.Threading.Timer class or System.Windows.Threading.DispatcherTimer class, which is the part of WPF. I have created an application to show you how to use the API. Let’s take a look how you can implement timer using System.Threading.Timer class. First of all, it has to be initialized. 1: private Timer timer;
2:
3: public MainWindow()
4: {
5: // Form initialization code
6:
7: timer = new Timer(OnTimer, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
8: }
Timer’s constructor accepts four parameters. The first one is the callback method which is executed when timer ticks. I will show it to you soon. The second parameter is a state which is passed to the callback. It is null because there is nothing to pass this time. The third parameter is the amount of time to delay before the callback parameter invokes its methods. I use System.Threading.Timeout helper class to represent infinite timeout which simply means the timer is not going to start at the moment. And the final fourth parameter represents the time interval between invocations of the methods referenced by callback. Infinite timeout timespan means the callback method will be executed just once.
Well, the timer has been created. Let’s take a look how you can start the timer.
1: private void StartTimer(object sender, RoutedEventArgs e)
2: {
3: timer.Change(TimeSpan.Zero, new TimeSpan(0, 0, 1));
4:
5: // Disable the start buttons and enable the reset button.
6: }
The timer is started by calling its Change method. It accepts two arguments: the amount of time to delay before the invoking the callback method and the time interval between invocations of the callback. TimeSpan.Zero means we start the timer immediately and TimeSpan(0, 0, 1) tells the timer to tick every second.
There is one method hasn’t been shown yet. This is the callback method OnTimer which does a simple task: it shows current time in the center of the screen. Unfortunately you cannot simple write something like this:
1: clock.Content = DateTime.Now.ToString("hh:mm:ss");
The reason is Timer runs callback method on a separate thread, and it is not possible to access GUI controls from a non-GUI thread. You can avoid the problem using System.Windows.Threading.Dispatcher class.
1: private void OnTimer(object state)
2: {
3: Dispatcher.Invoke(() => ShowTime());
4: }
5:
6: private void ShowTime()
7: {
8: clock.Content = DateTime.Now.ToString("hh:mm:ss");
9: }
You can build similar application using System.Windows.Threading.DispatcherTimer class. The class represents a timer which is integrated into the Dispatcher queue. It means that your callback method is executed on GUI thread and you can write a code which updates your GUI components directly.
1: private DispatcherTimer dispatcherTimer;
2:
3: public MainWindow()
4: {
5: // Form initialization code
6:
7: dispatcherTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 1) };
8: dispatcherTimer.Tick += OnDispatcherTimer;
9: }
Dispatcher timer has nicer and cleaner API. All you need is to specify tick interval and Tick event handler. The you just call Start method to start the timer.
private void StartDispatcher(object sender, RoutedEventArgs e)
{
dispatcherTimer.Start();
// Disable the start buttons and enable the reset button.
}
And, since the Tick event handler is executed on GUI thread, the code which sets the actual time is straightforward.
1: private void OnDispatcherTimer(object sender, EventArgs e)
2: {
3: ShowTime();
4: }
We’re almost done. Let’s take a look how to stop the timers.
It is easy with the Dispatcher Timer.
1: dispatcherTimer.Stop();
And slightly more complicated with the Timer. You should use Change method again.
1: timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
What is the best way to add timer into an application?
The Dispatcher Timer has simple interface, but its advantages are disadvantages at the same time. You should not use it if your Tick event handler executes time-consuming operations. It freezes your window which it is executing the event handler method. You should think about using System.Threading.Timer in this case.
The code is available on GitHub.