Friday, October 16, 2009

WinForms/WPF Timers

I find myself using a lot of timers when doing GUI programming. Usually these timers are used to invoke a block of code only once. Sometimes there’s a good reason to delay a call, say you show a tooltip for 1sec then hide it (though in WPF animations should be used). Usually it’s to solve a tricky problem, like you have to let the callstack unwind for some dependent state to take effect. This is usually considered bad practice and there are very few times when no other legitimate techniques can be used. However when deadlines start closing in the number of timers increase. I like to think of Timers as the duct tape of GUI programming.

Using timers, especially for one-off calls is tedious and verbose

DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Tick += (delegate(object s, EventArgs args)
{
    timer.Stop();
    timer = null;
    DoSomethingCool();
});
timer.Start();

SetTimeout to the rescue.

The HTML DOM has a handy method called setTimeout. It's really easy to use, just pass it a function (or string) and the timeout value. That's it. There is no such time as setTimeout in .NET. But with some Extension methods combined with Lambda expressions we can get pretty close:
new Action(() => DoSomethingCool()).SetTimeout(10);

Here's the code. It comes in 2 flavors: Windows Forms and WPF

namespace SetTimeout.Wpf
{
    using System;
    using System.Windows.Threading;
 
    public static class SettimeoutDelegateExtension
    {
        /// <summary>
        /// Inspired by HTML DOM, executes a delegate via a DispatcherTimer once.
        /// </summary>
        /// <example>new Action(() => someObject.DoSomethingCool()).SetTimeout(100);
        /// </example>
        /// <remarks>Frequently things need to be executed in a timeout, but constructing a Timer is a pain especially for
        /// one-off calls. Combined with Lambda expressions this makes the whole process relativey painless.
        /// </remarks>
        /// <param name="action">Any delegate to execute</param>
        /// <param name="timeout">How long to wait to execute</param>
        /// <param name="args">Any arguments to pass to the delegate</param>
        public static void SetTimeout(this Delegate action, TimeSpan timeout, params object[] args)
        {
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = timeout;
            timer.Tick += new EventHandler(delegate(object sender, EventArgs e)
            {
                timer.Stop();
                timer = null;
                action.DynamicInvoke(args);
            });
            timer.Start();
        }
 
        /// <summary>
        /// Inspired by HTML DOM, executes a delegate via a DispatcherTimer once.
        /// </summary>
        /// <example>new Action(() => someObject.DoSomethingCool()).SetTimeout(100);
        /// </example>
        /// <remarks>Frequently things need to be executed in a timeout, but constructing a Timer is a pain especially for
        /// one-off calls. Combined with Lambda expressions this makes the whole process relativey painless.
        /// </remarks>
        /// <param name="action">Any delegate to execute</param>
        /// <param name="timeout">How long to wait to execute in milliseconds</param>
        /// <param name="args">Any arguments to pass to the delegate</param>
        public static void SetTimeout(this Delegate action, int timeout, params object[] args)
        {
            SetTimeout(action, TimeSpan.FromMilliseconds(timeout), args);
        }
    }
}

p.s. I hope you enjoy the pretty print. I spent way too much time on it

2 comments:

Alex said...

this is so awesome , i used to do this with javascript to display a progress bar, i love it!

vincentadell said...

WinForms UI controls for .NET applications