2011-12-10 18 views
5

He estado jugando con los nuevos patrones async CTP y MVVM. He estado convirtiendo un viejo programa mío que estaba utilizando un trabajador de segundo plano e informo sobre el progreso para actualizar una colección en mi modelo. He convertido a algo así como lo queasync Task.Run con MVVM

TaskEx.Run(async() => 
{ 
    while (true) 
    { 
    // update ObservableCollection here 
    } 
    await TaskEx.Delay(500); 
}); 

En mi opinión, ato a mi modelo de vista que expone esta colección observable. Sin embargo, cuando las actualizaciones de mi colección recibo la siguiente Excepción

Este tipo de CollectionView no admite cambios en su SourceCollection de un hilo diferente del hilo Dispatcher.

No estoy seguro de cuál es la forma correcta de extraer el hilo de la interfaz de usuario cuando se hace así.

+0

Como nota al margen, hay una versión actualizada del 'async' en el Visual Studio 11 Developer Preview que se puede descargar. – svick

Respuesta

5

No tiene que ejecutar métodos asíncronos usando Task.Run(), o cualquier otro medio especial, simplemente llámelos. Y en tu caso, eso es exactamente lo que está causando el problema.

función dada como esto:

Action f = async() => 
{ 
    while (true) 
    { 
     // modify the observable collection here 
     await Task.Delay(500); 
    } 
}; 

Llamarlo como esto de algún método de ejecución en el subproceso de interfaz de usuario, como un controlador de eventos:

f(); 

funciona exactamente como debe ser. Ejecuta la primera iteración del ciclo y luego regresa. La siguiente iteración se ejecuta después de 500 ms (o más, si el hilo de la interfaz de usuario está ocupado) en el hilo de la interfaz de usuario.

Por otro lado, si se llama así:

Task.Run(addNames); 

no funciona correctamente. La razón para esto es que los métodos async intentan continuar en el mismo contexto en el que se iniciaron (a menos que especifiques lo contrario explícitamente). La primera versión se inició en el subproceso de interfaz de usuario, por lo que continuó en el hilo de la interfaz de usuario. La segunda versión comenzó en un hilo ThreadPool (gracias a Task.Run()) y continuó allí también. Por eso causó tu error.

Todo esto se hace usando SynchronizationContext, si hay uno presente.

3

Creó un ObservableCollection en el hilo de la interfaz de usuario principal, y está intentando actualizarlo en un subproceso de fondo asíncrono, que no puede hacer en WPF.

Como alternativa, obtenga los resultados de un hilo de fondo y agréguelos al ObservableCollection en el hilo de la interfaz de usuario principal.

Por lo general, mi código para actualizar una ObservableCollection en un subproceso de fondo se verá algo como esto:

private async void LoadItems() 
{ 
    Task<List<MyItem>> getItemsTask = Task.Factory.StartNew(GetItems); 

    foreach(MyItem item in await getItemsTask) 
     MyCollection.Add(item); 
} 

private List<MyItem> GetItems() 
{ 
    // Make database call to get items 
} 
2

Si bien es cierto que no es capaz de actualizar una ObservableCollection de un segundo hilo, es posible para crear una colección observable asincrónica. Esto le permite actualizar la colección desde dentro de las tareas o en los hilos donde no se creó la colección.

Publicaba el ejemplo, pero encontré la información aquí. Es bastante astuto, y encontré que es un ejemplo muy útil.

http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/