2012-04-26 38 views
5

Sé que hay algunas respuestas sobre este tema en SO, pero no puedo conseguir ninguna de las soluciones que funcionen para mí. Estoy intentando abrir una nueva ventana, desde un ICommand disparado desde dentro de una plantilla de datos. Ambos de los siguientes dará el error antes mencionado cuando la nueva ventana se crea una instancia (dentro de "nueva MessageWindowP"):"El hilo de llamada debe ser STA" solución

El uso de TPL/FromCurrentSynchronizationContextActualización: trabaja

public class ChatUserCommand : ICommand 
{ 
    public void Execute(object sender) 
    { 
     if (sender is UserC) 
     { 
      var user = (UserC)sender; 
      var scheduler = TaskScheduler.FromCurrentSynchronizationContext();     
      Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler);   
     } 
    } 

    private void CreateMessageWindow(object o) 
    { 
     var user = (UserC)o; 
     var messageP = new MessageWindowP(); 
     messageP.ViewModel.Participants.Add(user); 
     messageP.View.Show(); 
    } 
} 

Usando ThreadStart:Actualización: no recomendado, consulte la respuesta de Jon

public class ChatUserCommand : ICommand 
{ 
    public void Execute(object sender) 
    { 
     if (sender is UserC) 
     { 
      var user = (UserC)sender; 

      var t = new ParameterizedThreadStart(CreateMessageWindow); 
      var thread = new Thread(t); 
      thread.SetApartmentState(ApartmentState.STA); 
      thread.Start(sender);   
     } 
    } 

    private void CreateMessageWindow(object o) 
    { 
     var user = (UserC)o; 
     var messageP = new MessageWindowP(); 
     messageP.ViewModel.Participants.Add(user); 
     messageP.View.Show(); 
    } 
} 

Gracias

EDITAR. En base a las respuestas hasta ahora, me gustaría señalar que también he probado BeginInvoke en el despachador actual, así como también he ejecutado el código en el método original (así es como comenzó el código). Vea a continuación:

BeginInvokeUpdate: No se recomienda ver la respuesta de Jon

public class ChatUserCommand : ICommand 
{ 
    public void Execute(object sender) 
    { 
     if (sender is UserC) 
     { 
      var user = (UserC)sender; 
      Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender);  
     } 
    } 

    private void CreateMessageWindow(object o) 
    { 
     var user = (UserC)o; 
     var messageP = new MessageWindowP(); 
     messageP.ViewModel.Participants.Add(user); 
     messageP.View.Show(); 
    } 
} 

En mismo hiloActualización: funciona si usted está en hilo de interfaz de usuario ya

public class ChatUserCommand : ICommand 
{ 
    public void Execute(object sender) 
    { 
     if (sender is UserC) 
     { 
      var user = (UserC)sender; 
      var messageP = new MessageWindowP(); 
      messageP.ViewModel.Participants.Add(user); 
      messageP.View.Show();  
     } 
    } 

} 

BeginInvoke, haciendo referencia al despachador de first/main wi NDOWActualización: trabaja

public void Execute(object sender) 
    { 
     if (sender is UserC) 
     { 
      var user = (UserC)sender; 
        GeneralManager.MainDispatcher.BeginInvoke(
           DispatcherPriority.Normal, 
           new Action(() => this.CreateMessageWindow(user)));  
     } 
    } 

donde GeneralManager.MainDispatcher es una referencia al despachador de la primera ventana creo:

 [somewhere far far away] 
     mainP = new MainP(); 
     MainDispatcher = mainP.View.Dispatcher; 

Estoy en una pérdida.

+1

¿Y cuál era el problema de llamar en el mismo hilo y 'BeginInvoke'? ¿En qué hilo se ejecuta el 'Execute'? – Vlad

+1

No puede usar 'Dispatcher.CurrentDispatcher' como lo hace aquí. Ver la actualización de mi respuesta. – Jon

+0

Chicos. Todavía no hay suerte ... :(ver actualización de nuevo –

Respuesta

7

El hilo de llamada no debe ser solo ser STA, pero también debe tener un bucle de mensaje. Solo hay un hilo en su aplicación que ya tiene un bucle de mensaje, y ese es su hilo principal. Por lo tanto, debe usar Dispatcher.BeginInvoke para abrir su ventana desde su hilo principal.

E.g. si usted tiene una referencia a la ventana principal de la aplicación (MainWindow), se puede hacer

MainWindow.BeginInvoke(
    DispatcherPriority.Normal, 
    new Action(() => this.CreateMessageWindow(user))); 

Actualización:CUIDADO: que no puede ciegamente llamar Dispatcher.CurrentDispatcher porque no hace lo que cree que hace. El documentation dice que CurrentDispatcher:

Obtiene el despachador para el subproceso en ejecución actualmente y crea una nueva Dispatcher si uno no esté asociada con el hilo.

Es por eso que debe utilizar el Dispatcher asociado con un control de interfaz de usuario ya existente (como su ventana principal, como en el ejemplo anterior).

+0

¿Alguna otra idea, Jon? intenté usar el Dispatcher de mi ventana principal. –

3

Está intentando crear una ventana a partir de un hilo de fondo. No puede hacerlo debido a una variedad de razones. Por lo general, necesita crear la ventana en el hilo principal de la aplicación.

Para su caso, una simple idea sería simplemente lo hacen inmediatamente (solo llame CreateMessageWindow dentro Execute) en lugar de asignar un Task, debido a su comando definitivamente el fuego desde el hilo principal (IU). Si no está seguro acerca del subproceso donde se ejecuta Execute, puede ordenarlo a la secuencia de la interfaz de usuario utilizando Dispatcher.BeginInvoke().

Hay muy pocos casos en que desee que su nueva ventana se ejecute en un hilo no principal. Si esto realmente se necesita en su caso, debe agregar Dispatcher.Run(); después de messageP.View.Show(); (usando la segunda variante del código). Esto inicia el ciclo de mensajes en el nuevo hilo.

No intente ejecutar la ventana en el hilo de TPL, ya que estos hilos suelen ser subprocesos de subprocesos y, por lo tanto, están fuera de su control. Por ejemplo, no puede asegurarse de que sean STA (generalmente, son MTA).

Editar:
desde el error en su código actualizado, está claro que el Execute se ejecuta en algún subproceso no relacionado con la interfaz de usuario. Intente usar Application.Current.Dispatcher en lugar de Dispatcher.CurrentDispatcher. (CurrentDispatcher significa que el despachador del hilo actual, que puede ser malo si el hilo actual no es el principal.)

+0

Hola. Gracias por su ayuda. Todo lo que dices tiene sentido, pero solo recurrí a esas técnicas porque el código dentro de Execute causó el mismo error. Vea la pregunta actualizada para más cosas que he intentado. –

+0

@Harry: actualizó la respuesta también. – Vlad

Cuestiones relacionadas