1

I have a WPF application and I am working with the .NET Framework 4.0 and C#. My application consists of an interface with several controls. In particular I have a task that needs to be executed periodically every 10 seconds. In order to execute it I use a System.Windows.Threading.DispatcherTimer. The ViewModel looks like this:

public class WindowViewModel {
  protected DispatcherTimer cycle;

  public WindowViewModel() {
    this.cycle = new DispatcherTimer(DispatcherPriority.Normal, 
                 System.Windows.Application.Current.Dispatcher);
    this.cycle.Interval = new TimeSpan(0,0,0,0,10000);
    this.cycle.Tick += delegate(object sender, EventArgs e) {
      for (int i = 0; i < 20; i++) {
        // Doing something
      }
    };
    this.cycle.Start;
  }
}

As I said the routine called periodically does something. In particular there is some heavy logic there which causes that routine to take some seconds to execute and complete. Well it is a different thread so I should be ok and the interface is not supposed to freeze.

The problem is that that routine causes the viewmodel to be updated. Several data are updated, and the corresponding View is bound to those data. What happens is that all updated data are refreshed once at a time when the routine completes. I want data to be updated during the thread execution.

In particular inside that routine I have a for cycle. Well at the exit of the cycle everything is updated in the interface. How to achieve this? Where am i doing wrong?

Andry
  • 16,172
  • 27
  • 138
  • 246
  • What makes you think "it is a different thread"? – spender Jun 14 '13 at 10:15
  • "it is a different thread" no, it is main UI thread. Use regular System.Threading.Timer to execute tick routine in different thread. – FireAlkazar Jun 14 '13 at 10:16
  • So it is the same thread? But if I do like you said I will face this problem here: http://stackoverflow.com/questions/2137769/where-do-i-get-a-thread-safe-collectionview – Andry Jun 14 '13 at 10:20
  • @Andry: I've added more to my answer to address this comment. – spender Jun 14 '13 at 10:34

1 Answers1

2

The DispatcherTimer uses the supplied Dispatcher to run the timer callback.

If you take a look at the docs for Dispatcher, there's a clue:

Provides services for managing the queue of work items for a thread.

So, by using the System.Windows.Application.Current.Dispatcher, you're using the Dispatcher that manages "the queue of work items" for the UI thread.

To run your work in the ThreadPool instead, you could either use System.Threading.Timer or use ThreadPool.QueueUserWorkItem in your DispatcherTimer callback.

If you combine this with the following extension method, it becomes easy to marshal any UI specific stuff back to the Dispatcher when you finish your heavy workload:

public static class DispatcherEx
{
    public static void InvokeOrExecute(this Dispatcher dispatcher, Action action)
    {
        if (dispatcher.CheckAccess())
        {
            action();
        }
        else
        {
            dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                   action);
        }
    }
}

then...

this.cycle.Tick += delegate(object sender, EventArgs e) {
  ThreadPool.QueueUserWorkItem(_ => {
     for (int i = 0; i < 20; i++) {
       // Doing something heavy
       System.Windows.Application.Current.Dispatcher.InvokeOrExecute(() => {
          //update the UI on the UI thread.
       });
     }
  });
};
spender
  • 117,338
  • 33
  • 229
  • 351
  • Well, It throws me some exceptions on the index I use in the for cycle, it says that the collection on which a datagrid binds on has less elements of the specified index => IndexOutOfBoundException... Is it because I am not guarding the collection when it is accessed? I need a mutex? But it is not like something else is doing anything with the collection other than that thread... maybe the binding from the View... – Andry Jun 14 '13 at 17:35
  • However it is because of some other aspects I didn't detail in the post... you gave me a really good advice, I will try to make this thing work applying your pattern. – Andry Jun 15 '13 at 09:15