43

Please, explain the difference between "DispatcherTimer" and "a regular Timer" that @Kent Boogaart meant for using in a multithreading WPF app as a task sheduler in this topic:

Advice needed for multi-threading strategy for WPF application

in the commentaries to one of the post (quote):

-If all the DispatcherTimer does is kick off another thread, what is the point of using the DispatcherTimer? ....those threads don't need to be started on the UI thread. You could just use a regular Timer and avoid interrupting the UI altogether

What is "a regular timer" that is meant? How they ("DispatcherTimer" and "a regular Timer") differ regarding their impact on UI?

(Until reading this post I thought about DispatcherTimer as a natural way of using timers in WPF. What is the cases when this is not true?)

Community
  • 1
  • 1
rem
  • 16,745
  • 37
  • 112
  • 180

3 Answers3

67

DispatcherTimer is the regular timer. It fires its Tick event on the UI thread, you can do anything you want with the UI. System.Timers.Timer is an asynchronous timer, its Elapsed event runs on a thread pool thread. You have to be very careful in your event handler, you are not allowed to touch any UI component or data-bound variables. And you'll need to use the lock statement where ever you access class members that are also used on the UI thread.

In the linked answer, the Timer class was more appropriate because the OP was trying to run code asynchronously on purpose.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • note that 'touching' includes setting MVVM viewmodel properties so when using a DispatcherTimer you're fine, but with a Timer you can't even do that – Simon_Weaver Sep 15 '12 at 04:19
  • @Simon_Weaver If it's a basic string/number/Boolean property then changing the value in the timer's callback will work just fine, even if the callback doesn't run on the UI thread. It is true that you can encounter problems with something like an `ObservableCollection` property though. – Steven Rands May 23 '20 at 18:27
36

Regular Timer's Tick event is actually fired on the thread where the Timer was created, so in the tick event, in order to access anything with the UI, you will have to go through dispatcher.begininvoke as mentioned below.

RegularTimer_Tick(object sender, EventArgs e)
{
    txtBox1.Text = "count" + i.ToString(); 
    // error can not access
    // txtBox1.Text property outside dispatcher thread...

    // instead you have to write...
    Dispatcher.BeginInvoke( (Action)delegate(){
       txtBox1.Text = "count " + i.ToString();
    });
}

In case of Dispatcher Timer, you can access UI elements without doing begin invoke or invoke as follow...

DispatcherTimer_Tick(object sender, EventArgs e)
{
    txtBox1.Text = "Count " + i.ToString();
    // no error here..
}

DispatcherTimer just provides convenience over Regular Timer to access UI objects easily.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • is dispatcher Timer using Invoke or BeginInvoke? – Martin Ch Apr 12 '16 at 09:23
  • Based on the source here, DispatcherTimer is using BeginInvoke, reason is, dispatcher queue has priority based invoke mechanism. http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,d2a05f6b09eee858 – Akash Kava Apr 12 '16 at 10:50
21

With .NET 4.5 you can also create an async delegate for your timer if you need to use the new .NET 4.5 await functionality.

        var timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(20);
        timer.Tick += new EventHandler(async (object s, EventArgs a) =>
        {
            Message = "Updating...";
            await UpdateSystemStatus(false);  
            Message = "Last updated " + DateTime.Now;              
        });
        timer.Start();
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • this may also be of interest (my own question seeking an async friendly DispatcherTimer) http://stackoverflow.com/questions/12442622/async-aware-friendly-dispatchertimer-wrapper-subclass/12442719#12442719 – Simon_Weaver Sep 15 '12 at 21:32