Often in a Silverlight client you will need to delay the execution of a particular action. There is no intrinsic setTimeout function like in JavaScript window object but you can use System.Windows.Threading.DispatcherTimer object. If you are looking for a tidy wrapper around this object you may want to consider this solution:

public static class UIHelper
{
    public static void SetTimeout(int milliseconds, Action func)
    {
        var timer = new DispatcherTimerContainingAction
                        {
                            Interval = new TimeSpan(0, 0, 0, 0, milliseconds),
                            Action = func
                        };
        timer.Tick += _onTimeout;
        timer.Start();
    }

    private static void _onTimeout(object sender, EventArgs arg)
    {
        var t = sender as DispatcherTimerContainingAction;
        t.Stop();
        t.Action();
        t.Tick -= _onTimeout;
    }
}

public class DispatcherTimerContainingAction : System.Windows.Threading.DispatcherTimer
{
    /// <summary>
    /// uncomment this to see when the DispatcherTimerContainingAction is collected
    /// if you remove  t.Tick -= _onTimeout; line from _onTimeout method
    /// you will see that the timer is never collected
    /// </summary>
    //~DispatcherTimerContainingAction()
    //{
    //    throw new Exception("DispatcherTimerContainingAction is disposed");
    //}

    public Action Action { get; set; }
}

Here is an example of how to use it to show an alert box after two seconds.

UIHelper.SetTimeout(2000, () =>{ 
  // do something here for example show alert box:
  HtmlPage.Window.Alert("2 seconds have passed");
});

As you can see it looks almost like window.setTimeout in JavaScript

Initially I had it without removing the event handler and this way the delegate reference was preventing the garbage collection of the timer. But after a comment from Zi Han (Thanks Zi!) I realized it was not the right way. If you want to be sure that now it is garbage collected you can uncomment the destructor.

Share this post:   digg     Stumble Upon     del.icio.us     E-mail

Pierre
Posted on 5/13/2008 1:21:16 PM

And you can probably use lambda expression with that?

Vladimir Bodurov
Posted on 5/13/2008 1:42:55 PM

Yes absolutely, for example

UIHelper.SetTimeout(500, () => HtmlPage.Window.Alert("test"));

Zi Han
Posted on 12/18/2008 3:02:32 AM

Thanks for this piece of code. One question - is there a need to somehow "dispose" of the timer after it has been used.. for memory management purposes?

Vladimir Bodurov
Posted on 12/18/2008 3:17:55 AM

Thank you Zi, you are right we have to add the removal of the delegate. The code is now updated accordingly

Geoff McGrath
Posted on 5/7/2009 2:00:36 AM

window.setTimeout returns an integer -- you can use that with window.clearTimeout( int value ) to cancel a timer...I imagine this could be done using the GetHashCode...use that as a key in a dictionary, something like this:

private static Dictionary<int,DispatcherTimerContainingAction> m_Timers;

If you have SetTimeout return an int, and implement a ClearTimeout method that takes an int...wouldn't that do the trick?

Vladimir Bodurov
Posted on 5/7/2009 8:23:28 AM

for sure, the only issue is that I expect it will be more resource intense than the solution internal to Silverlight because you'd have to go through the JavaScript - Silverlight bridge each time

Geoff McGrath
Posted on 5/7/2009 1:50:28 PM

Oh...My thought was to do it within Silverlight, here's the code within your solution:

public static class UIHelper 
{ 
		private static Dictionary<int,DispatcherTimerContainingAction> m_Timers; 
 
		// static constructor 
		static UIHelper() 
		{ 
			m_Timers = new Dictionary<int,DispatcherTimerContainingAction>(); 
		} 
		 
		public static int SetTimeout(int milliseconds, Action func) 
		{ 
			int timerKey = new Random().Next(0,1000000); 
			var timer = new DispatcherTimerContainingAction 
							{ 
								Interval = new TimeSpan(0, 0, 0, 0, milliseconds), 
								Action = func, 
							}; 
 
			m_Timers.Add( timer.GetHashCode(), timer ); 
			timer.Tick += _onTimeout; 
			timer.Start(); 
			return timer.GetHashCode(); 
		} 
 
		public static bool ClearTimeout( int TimerHashCode ) 
		{ 
			bool retVal = false; 
			try 
			{ 
				DispatcherTimerContainingAction timer = m_Timers[TimerHashCode]; 
				timer.Stop(); 
				timer.Action = null; 
				timer.Tick -= _onTimeout; 
				if( m_Timers.Remove(TimerHashCode) ) 
				{ 
					retVal = true; 
				} 
				timer = null; 
			} 
			catch(KeyNotFoundException) 
			{ 
			} 
			return retVal; 
		} 
 
		private static void _onTimeout(object sender, EventArgs arg) 
		{ 
			var t = sender as DispatcherTimerContainingAction; 
			try 
			{ 
				m_Timers.Remove(t.GetHashCode()); 
			} 
			catch(ArgumentNullException) 
			{ 
			} 
			t.Stop(); 
			t.Action(); 
			t.Tick -= _onTimeout; 
		} 
	} 
 
	public class DispatcherTimerContainingAction : System.Windows.Threading.DispatcherTimer 
} 
Vladimir Bodurov
Posted on 5/7/2009 3:30:07 PM

Ah OK, I didn't understand you at first.

If you need to cancel it, then that seems as a reasonable solution. Thanks for sharing the code!

Franck
Posted on 5/19/2010 7:18:16 PM

i just wanna say thank you for posting this code...

i had a problem for a game that i m developping for the windows phone and the color was always late... so i had to delay a function ...

thank you again

Rodney
Posted on 7/16/2010 5:14:29 PM

Hi Geoff,

I am trying to use your code in the comments above to cancel timers - I use it to show a popup window (which has a close button) and I want to cancel the event if it is closed.

I am using the tag property of the window to store it's hashcode eg.
mainPage.windowAppMessages.Tag = UITimer.SetTimeout(secondsToShow * 1000, () =>

I then pass this back in to the cancel event to close it - but I am getting some strange results/browser crashes - does this sound ok?

Thanks!

Manjunatha S
Posted on 8/25/2010 3:44:06 AM

Hey,
Thanks for this code .. It helped me for my development

Rams
Posted on 11/13/2010 7:11:53 AM

Vladimir,
Thanks for the tip. Just what I was looking for.

Geoff,
thanks for the additions.

D rake
Posted on 11/23/2010 11:22:07 AM

Pretty simple code , i`m looking forword for more tutorials sir.

bcm software
Posted on 4/18/2011 6:48:19 AM

Thanks for the tutorial on how to delay an action when using Silverlight. I have come across this problem many times and did not know how to work around it. I never knew about the System.Windows.Threading.DispatcherTimer object. I have a feeling I will be using this a lot in my upcoming projects.

Tarek
Posted on 7/5/2011 6:59:29 PM

thanks a lot

Commenting temporarily disabled