2010-03-04 15 views
36

En mi aplicación WPF hago algunas comunicaciones asíncronas (con servidor). En la función de devolución de llamada, termino creando objetos InkPresenter a partir del resultado del servidor. Esto requiere que el hilo en ejecución sea STA, que al parecer actualmente no lo es. Por lo tanto, recibo la siguiente excepción:¿Cómo ejecutar algo en el hilo STA?

No se puede crear una instancia de 'InkPresenter' definida en el ensamblaje [..] El hilo que llama debe ser STA, porque muchos componentes de la interfaz de usuario lo requieren.

Actualmente mi asíncrono llamada a la función es la siguiente:

public void SearchForFooAsync(string searchString) 
{ 
    var caller = new Func<string, Foo>(_patientProxy.SearchForFoo); 
    caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null); 
} 

¿Cómo puedo hacer la devolución de llamada - que hará la creación InkPresenter - STA ser? O invoque el análisis XamlReader en un nuevo hilo STA.

public void SearchForFooCallbackMethod(IAsyncResult ar) 
{ 
    var foo = GetFooFromAsyncResult(ar); 
    var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; // <!-- Requires STA 
    [..] 
} 
+0

¿Qué pasó con '[STATread]' antes de un método? No siempre es apropiado, pero es muy fácil. Tal vez no salió hasta 2011? No lo he usado desde 2011 que recuerdo ... – ebyrob

Respuesta

52

Puede iniciar subprocesos STA así:

Thread thread = new Thread(MethodWhichRequiresSTA); 
    thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA 
    thread.Start(); 
    thread.Join(); //Wait for the thread to end 

El único problema es que su objeto de resultado se debe pasar a lo largo de alguna manera .. Puede utilizar un campo privado para que, o sumergirse en pasar a lo largo parámetros en trapos. ¡Aquí configuro los datos foo en un campo privado y comienzo el hilo STA para mutar el inkpresenter!

private var foo; 
public void SearchForFooCallbackMethod(IAsyncResult ar) 
{ 
    foo = GetFooFromAsyncResult(ar); 
    Thread thread = new Thread(ProcessInkPresenter); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    thread.Join(); 
} 

private void ProcessInkPresenter() 
{ 
    var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; 
} 

Hope this helps!

+0

Parece muy útil, ¡gracias! Trataré de aplicar esto a mi código. – stiank81

+0

Gracias :) Avíseme si solucionará el problema. ¡Usamos esta técnica para generar imágenes PNG de nuestros controles Xaml en el servidor! – Arcturus

+0

Parece que solucionó el problema, pero acaba de resolver otro problema. * Suspiro * ... Se marcará como aceptado en cuanto tenga todo funcionando aquí ... ¡Gracias! – stiank81

3

Debería ser lo suficientemente bueno para llamarlo en el hilo de la interfaz de usuario. Por lo tanto, use un BackgroundWorker y en el RunWorkerAsyncCompleted, luego puede hacer la creación del inkPresenter.

+0

Tienes razón. El problema es que la devolución de llamada no se ejecuta en el hilo de la interfaz de usuario. El subproceso de la interfaz de usuario se ejecuta con STA, por lo que ejecutarlo en el subproceso de la interfaz de usuario debería resolverlo por mí. – stiank81

11

Puede usar la clase Dipatcher para ejecutar la llamada al método en el UI-Thread. El Dispatcher proporciona la propiedad estática CurrentDispatcher para obtener el despachador de un hilo.

Si su objeto de la clase, que crea el InkPresenter, se crea en el UI-Thread, entonces el método CurrentDispatcher devuelve el Dispatcher del UI-Thread.

En el Dispatcher puede invocar el método BeginInvoke para llamar al delegado especificado de forma asincrónica en el hilo.

+1

Dispatcher.Invoke o BeginInvoke es el camino a seguir. Mucho más simple que la solución aceptada – GameAlchemist

1

Es un poco de un truco, pero yo usaría XTATestRunner lo que el código se verá así:

public void SearchForFooAsync(string searchString) 
    { 
     var caller = new Func<string, Foo>(_patientProxy.SearchForFoo); 
     caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null); 
    } 

    public void SearchForFooCallbackMethod(IAsyncResult ar) 
    { 

      var foo = GetFooFromAsyncResult(ar); 
InkPresenter inkPresenter; 
      new XTATestRunner().RunSTA(() => { 
         inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; 
        }); 
    } 

como un bono que es posible capturar excepciones lanzadas en STA (o MTA) de hilo de esta manera:

try{ 
new XTATestRunner().RunSTA(() => { 
         throw new InvalidOperationException(); 
        }); 
} 
catch(InvalidOperationException ex){ 
} 
Cuestiones relacionadas