Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Portable class library equivalent of Dispatcher.Invoke or Dispatcher.RunAsync

In .NET, Windows 8 and Windows Phone 7 I have code similar to this:

public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
    if (dispatcher.CheckAccess())
    {
        action();
    }
    else
    {
        dispatcher.Invoke(action);
    }
}

How would I do something in the portable class library? It would be nice to have one platform agnostic implementation of this. My idea is to use the TPL which is not available in WP7 but definitely will be soon.

// PortableDispatcher must be created on the UI thread and then made accessible 
// maybe as a property in my ViewModel base class.
public class PortableDispatcher
{
    private TaskScheduler taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

    public void Invoke(Action action)
    {
        if (Alread on UI thread. How would I do this.)
        {
            action();
        }

        Task.Factory.StartNew(
            action, 
            CancellationToken.None,
            TaskCreationOptions.None,
            taskScheduler);
    }
}

The only thing I'm unsure about is what the performance implications of this would be. Perhaps I'll do some tests.

like image 225
Muhammad Rehan Saeed Avatar asked Jun 29 '12 08:06

Muhammad Rehan Saeed


2 Answers

You can use SynchronizationContext.Post or Send method. It is portable, and when you use a UI framework with a dispatcher then the current synchronization context will delegate the work to the Dispatcher.

Specifically, you can use the following code:

void InvokeIfRequired(this SynchroniationContext context, Action action)
{
    if (SynchroniationContext.Current == context)
    {
        action();
    }
    else
    {
        context.Send(action) // send = synchronously
        // context.Post(action)  - post is asynchronous.
    }  
}
like image 134
Alex Shtof Avatar answered Oct 18 '22 02:10

Alex Shtof


With the advent of the TPL. I came up with a slightly improved version of the accepted answer which returns a task and can be await'ed using the new async and await keywords.

public Task RunAsync(Action action)
{
    TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();

    if (this.synchronizationContext == SynchronizationContext.Current)
    {
        try
        {
            action();
            taskCompletionSource.SetResult(null);
        }
        catch (Exception exception)
        {
            taskCompletionSource.SetException(exception);
        }
    }
    else
    {
        // Run the action asyncronously. The Send method can be used to run syncronously.
        this.synchronizationContext.Post(
            (obj) => 
            {
                try
                {
                    action();
                    taskCompletionSource.SetResult(null);
                }
                catch (Exception exception)
                {
                    taskCompletionSource.SetException(exception);
                }
            }, 
            null);
    }

    return taskCompletionSource.Task;
}
like image 20
Muhammad Rehan Saeed Avatar answered Oct 18 '22 02:10

Muhammad Rehan Saeed