4

I refactored my WPF code recently and now my DispatcherTimer stopped firing. I checked other similar posts here, but they all seemed to be problems with having the wrong dispatcher thread set, which I tried...

My code looks like this:

class MainWindow : Window
{
    private async void GoButton_Click(object sender, RoutedEventArgs e)
    {
        Hide();

        m_files = new CopyFilesWindow();
        m_files.Show();

        m_dispatcherTimer = new DispatcherTimer();
        m_dispatcherTimer.Tick += dispatcherTimer_Tick;
        m_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 250);
        m_dispatcherTimer.Start();

        await SomeLongRunningTask();

        m_files.Hide();
        Show();
    }

(The current class is my main Window object, which I hide for the duration of file copying. CopyFilesWindow is a simple Xaml window that contains controls I modify...CopyFilesWindow does absolutely nothing itself.)

Basically, I await a long running task (copying a bunch of large files), and my DispatcherTimer is supposed to update the progress in dispatcherTimer_Tick. However, I set a breakpoint on that function and it doesn't get hit.

I have also tried setting the Dispatcher with the constructor like so:

        m_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal, m_files.Dispatcher);
        m_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal, this.Dispatcher);

But neither of these things change the behavior...it still doesn't fire.

What am I doing wrong here?

LCC
  • 1,170
  • 1
  • 17
  • 28

1 Answers1

5

The DispatcherTime runs on the ... Dispatcher thread. Which is stuck waiting SomeLongRunningTask() to finish.

Indeed, when you press the button Go, it is the dispatcher thread which executes GoButton_Click. Thus, you should never make a method called by UI (the dispatcher thread) async.

private void GoButton_Click(object sender, RoutedEventArgs e)
{
    Hide();

    m_files = new CopyFilesWindow();
    m_files.Show();

    m_dispatcherTimer = new DispatcherTimer();
    m_dispatcherTimer.Tick += dispatcherTimer_Tick;
    m_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 250);
    m_dispatcherTimer.Start();

    SomeLongRunningTask.ContinueWith(() => 
    {
        // Executes this once SomeLongRunningTask is done (even if it raised an exception)
        m_files.Hide();
        Show();
    }, TaskScheduler.FromCurrentSynchronizationContext());  // This paramater is used to specify to run the lambda expression on the UI thread.
}
Cédric Bignon
  • 12,892
  • 3
  • 39
  • 51
  • Thank you! The way I structured my code made it impractical to use ContinueWith (the code above is just a simplification of what I'm doing). However, moving the entire contents of GoButton_Click into its own async method (and unmarking GoButton_Click as async) with a few other minor changes fixed the problem. I didn't realize I was 'await'ing on the dispatcher thread... Once I realized that was the issue it was easy to move the work to another thread. – LCC Jul 21 '13 at 22:45