Advertisement

DispatcherTimer not ticking on BackgroundWorker

Started by October 28, 2021 07:49 PM
2 comments, last by Dave Haylett 3 years, 1 month ago

Hi, I'm trying to put some animation code (simple things like increasing frame counters for sprites, or changing X/Y co-ords) into a dispatcher timer which sits in its own thread. This is so that the main loop draws the sprites as per the refresh rate of the monitor, but the animations/movements are refreshed every 1/60th sec regardless, so sprites don't animate/move twice as fast if you change monitor refresh from 60hz to 120hz (stupid example).

workerAnimation = new BackgroundWorker
{
	WorkerReportsProgress = false,
    WorkerSupportsCancellation = true
};
workerAnimation.DoWork += animationTimerStart;
workerAnimation.RunWorkerAsync();

void animationTimerStart(object sender, DoWorkEventArgs e)
{
	animationTimer = new DispatcherTimer();
	animationTimer.Tick += new EventHandler(animateSprites);
	animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 60);
	animationTimer.Start();
	do { } while (true);
}

void animateSprites(object sender, EventArgs e)
{
   sprite.frame++; // simple example
   sprite.x++;
}

The worker thread is set up ok, and loops infinitely in the do-loop (there's nothing else for this thread to do except tick the dispatcher timer), but the animationTimer never seems to tick, i.e. the animateSprites() function is never called. The main thread is running fine.

What am I doing wrong?

I guess you have a nasty mistake in your code which leads to the timer never fireing (or better, the dispatcher never getting a tick. The DispatcherTimer is a dispatcher object which in WPF is bound to a specific Dispatcher, the Dispatcher which is in the main thread for example. As far as I know and I'm not a WPF expert, you can have different Dispatcher instances but only the one in the main thread is handled by WPF as default. What you do in the BackgroundWorker ist to make use of another thread in order to create and initialize the DispatcherTimer instance.

!! This is wrong !!

By doing this, you attach the DispatcherTimer to the Dispatcher of the current thread, which doesn't exist. So the timer might tick but the Dispatcher is never called as from the docs:

If a System.Timers.Timer is used in a WPF application, it is worth noting that the System.Timers.Timer runs on a different thread than the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. Reasons for using a DispatcherTimer as opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer

And so the action is never handled or it even throws an uncought exception which you or the BackgroundWorker catch silently.

TL;DR initialize the DispatcherTimer on the main thread should solve your problems

Advertisement

Thanks Shaarigan. I put the DispatcherTimer back on the UI thread, but had to set its priority to Render, otherwise it wasn't being called (the screen render seemed to take up all the time…)

This topic is closed to new replies.

Advertisement