9

En .NET, Windows 8 y Windows Phone 7 Tengo un código similar a este:portátil biblioteca de clases equivalente de Dispatcher.Invoke o Dispatcher.RunAsync

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

¿Cómo iba a hacer algo en la biblioteca de clases portátil? Sería bueno tener una implementación agnóstica de esta plataforma. Mi idea es usar el TPL que no está disponible en WP7, pero definitivamente lo será pronto.

// 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); 
    } 
} 

Lo único que no estoy seguro es cuáles serán las implicaciones de rendimiento de esto. Quizás haga algunas pruebas.

Respuesta

2

Con el advenimiento de la TPL. Se me ocurrió una versión ligeramente mejorada de la respuesta aceptada que devuelve una tarea y se puede esperar usando la nueva función asíncrona y esperar palabras clave.

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; 
} 
+2

Nice. Parece que creó una clase, pero solo publicó el método, por lo que una nota (tal vez obvia) para los demás. this.synchronizationContext tiene que asignarse en la secuencia en la que desea que se ejecute la acción antes de llamar al método. De hecho, modifiqué un poco y creé una extensión: tarea pública RunAsync (este contexto SynchronizationContext, acción Acción). Ahora es más como una respuesta original, ya que puede ser independiente. Lo mejor de ambas respuestas. :) – Wes

13

Puede usar SynchronizationContext.Post o método de envío. Es portátil, y cuando utiliza un marco de interfaz de usuario con un despachador, el contexto de sincronización actual delegará el trabajo en el despachador.

En concreto, puede utilizar el siguiente código:

void InvokeIfRequired(this SynchroniationContext context, Action action) 
{ 
    if (SynchroniationContext.Current == context) 
    { 
     action(); 
    } 
    else 
    { 
     context.Send(action) // send = synchronously 
     // context.Post(action) - post is asynchronous. 
    } 
} 
+0

Nice one. ¿Cómo crearía el método de extensión que uso arriba para el despachador? Específicamente, cómo comprueba si ya se está ejecutando en el hilo de la interfaz de usuario en lugar de hacer dispatcher.CheckAccess(). –

+0

También es Publicar el Equivelant de BeginInvoke y Enviar el equivalente de Invoke? –

+1

No necesita verificar antes de publicar. Publicar ya hace los controles adecuados. –

Cuestiones relacionadas