Dispatcher.DelayedInvoke

There are many cases when a developer wants a delayed action to occur. This may be a simple timed reaction to a user event, a web service poll or retry loop, or any variety of task. If you’re feeling creative you could use a Storyboard or a complicated thread implementation, but I’d like to share an elegant solution that allows you to write delayed invokes on the UI thread like this:

Dispatcher.DelayedInvoke(TimeSpan.FromSeconds(30), () => { DoStuff(); });

This syntax is possible by leveraging the Extension Method notation. For those unfamiliar with extension methods, the general idea is that it is possible to define a static function where the first parameter is defined with a “this” keyword in front of it. This indicates to the compiler that any instance of the type of that first parameter should have the function applied to it. For example:

// Note the "this" prefix before the type specification.
public static string GetFullName(this User user)
{
	return String.Format("{0}, {1} {2}",
		user.LastName, user.FirstName, user.MiddleInitial);
}

public void TestFunction()
{
	User user = new User("John", String.Empty, "Doe");
	string fullName = user.GetFullName();
}

The real power of these extension methods is that you can add additional functionality to objects that are externally instantiated or that aren’t inheritable. The Dispatcher class is a perfect example of both. It is a sealed class, and the Dispatcher references exposed through the property on DependencyObjects is instantiated by the framework.

Getting back to the issue of the DelayedInvoke extension method, I decided to use a Thread with Sleep implementation so as not to use up a ThreadPool thread. There are many ways to implement a wait, and you can easily swap in another approach. Here is my full implementation:

public static class DispatcherExtensions
{
    // Invocation without arguments.
    public static void DelayedInvoke(this Dispatcher dispatcher, TimeSpan delay, Action action)
    {
        Thread thread = new Thread(DoDelayedInvokeByAction);
        thread.Start(new Tuple<Dispatcher, TimeSpan, Action>(dispatcher, delay, action));
    }

    private static void DoDelayedInvokeByAction(object parameter)
    {
        Tuple<Dispatcher, TimeSpan, Action> parameterData = (Tuple<Dispatcher, TimeSpan, Action>)parameter;

        Thread.Sleep(parameterData.Item2);

        parameterData.Item1.BeginInvoke(parameterData.Item3);
    }

    // Invocation with arguments.
    public static void DelayedInvoke(this Dispatcher dispatcher, TimeSpan delay, Delegate d, params object[] args)
    {
        Thread thread = new Thread(DoDelayedInvokeByDelegate);
        thread.Start(new Tuple<Dispatcher, TimeSpan, Delegate, object[]>(dispatcher, delay, d, args));
    }

    private static void DoDelayedInvokeByDelegate(object parameter)
    {
        Tuple<Dispatcher, TimeSpan, Delegate, object[]> parameterData = (Tuple<Dispatcher, TimeSpan, Delegate, object[]>)parameter;

        Thread.Sleep(parameterData.Item2);

        parameterData.Item1.BeginInvoke(parameterData.Item3, parameterData.Item4);
    }
}

It’s important to note that the thread work function calls the Dispatcher‘s BeginInvoke function. The main reason for exposing the DelayedInvoke function as an extension of the Dispatcher is that it is implicit that the callback be performed on the UI thread. Even when the DelayedInvoke function is called from a non-UI thread, all operations performed in the supplied delegate can be safely made against UI objects because of the Dispatcher.BeginInvoke call.

Hopefully this makes your life a little easier.

Advertisements

2 responses to “Dispatcher.DelayedInvoke

  • Tom Bean

    I’m developing a Silverlight 5 application and am using WCF Data Services to load data from a SQL Server database to customize the UI. I have tried various methods to retrieve the data before the UI becomes visible but have not been successful.

    Your DelayedInvoke extensions look like the solution, but, before I start experimenting, I would like to see if you have any sample code that implements the extensions to delay the UI.

    Currently, I am loading the data in the constructor of the App class in App.xmal.cs but because WCF Data Service queries are asynchronous, they return immediately and the UI displays.

    How would I use DelayedInvoke to postpone the UI until the data is loaded?

    Thanks,
    Tom

    • Code Thaumaturge

      Hi Tom,

      I suspect you’ve probably already found a solution, but your best bet is to use some manner of loading screen or control that you can put over the top of your UI region until the data is ready to be displayed. You can use a Storyboard to animate the loading overlay away once the data becomes available. This gives the user the benefit of knowing that the application is actually doing something and isn’t just failing to load.

      If your WCF service executes quickly and it’s just a question of the data needing to be available when the control loads, look into using Bindings. Leveraging the MVVM design pattern makes this process a lot easier.

      Hope your project is coming along nicely.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: