3

Within the App I display banners that rotate as time progresses or when user clicks next/previous.

1) I need to update an UI element within Timer.Elapsed method. From what I found, it seems that timer ticks are executed on their own thread and element's dispatcher should be use to update the UI.

( (Image)BannerPanel.Content ).Dispatcher.Invoke( new Action( () => {
    ( (Image)BannerPanel.Content ).Source = GetImage( new Uri( banner.Uri, UriKind.Absolute ) );
} ) );

but that throws an InvalidOperationException.

2) The banner controller provides Next and Previous methods, which, when called display next/previous banner respectively. As DisplayBanner method tries to display banner and when the file is not found it tries to re-download it using WebClient's AsyncFileDownload and on DownloadComplete it displays the image. Am I correct to assume that both, Elapsed fired by the timer and manual call of Next/Previous can occure at the same time, both being run on their own thread ( Previous/Next on UI thread and Elapsed on Timer thread ), possibly causing instability?

pikausp
  • 1,142
  • 11
  • 31
  • Just use a `DispatcherTimer` instead of a `Timer`. – Sheridan Sep 04 '14 at 15:11
  • There is a `Dispatcher` for *every* thread, so you might not be using the one from the UI thread. To ensure that you are, you can use the `Application.Current.Dispatcher` object. Also see [Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher](http://stackoverflow.com/questions/10448987/dispatcher-currentdispatcher-vs-application-current-dispatcher) for more information. However, the `DispatcherTimer` is the correct tool for this job as it avoids any need to use the `Dispatcher` object. – Sheridan Sep 04 '14 at 15:34

2 Answers2

3

You should look into using the DispatcherTimer class instead of a regular Timer. It's designed to be used with WPF UI for just these types of uses because it runs on the Dispatcher thread.

More info: System.Windows.Threading.DispatcherTimer

toadflakz
  • 7,764
  • 1
  • 27
  • 40
  • Alright, yet do you know what's the issue with the dispatcher call I was trying to do? Just for reference. – pikausp Sep 04 '14 at 15:17
  • @pikausp, see my comment on your question for that answer. This is currently the best answer in my opinion. – Sheridan Sep 04 '14 at 15:36
1

Try using Application instead,

Application.Current.Dispatcher.Invoke(new Action(() => 
{
    ((Image)BannerPanel.Content).Source = GetImage(new Uri(banner.Uri, UriKind.Absolute));
});
d.moncada
  • 16,900
  • 5
  • 53
  • 82
  • It doesn't crash anymore, but I switched to using DispatcherTimer as recommended by others. – pikausp Sep 04 '14 at 15:41
  • @pikausp, yeah, DispatcherTimer is more topical when it comes to your situation. Though, this is another alternative. I would recommend using Application.Current whenever you want to do an Invoke or BeginInvoke. – d.moncada Sep 04 '14 at 15:44