2011-12-10 12 views
5

Ho giocato con i nuovi modelli asincroni CTP e MVVM. Ho convertito un vecchio programma che utilizzava un worker in background e ho segnalato lo stato di avanzamento per aggiornare una raccolta nel mio modello. Ho convertito a qualcosa come cosìasync Task.Run con MVVM

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

A mio avviso mi legano alla mia ViewModel che espone questa collezione osservabile. Tuttavia, quando gli aggiornamenti della mia raccolta ottengo la seguente eccezione

Questo tipo di CollectionView non supporta le modifiche apportate a SourceCollection da un thread diverso dal thread Dispatcher.

Non sono sicuro di quale sia il modo corretto di tornare al thread dell'interfaccia utente quando viene eseguito in questo modo.

+0

Come nota a margine, c'è una versione aggiornata del 'async' nel Visual Studio 11 Developer Preview che è possibile scaricare. – svick

risposta

5

Non è necessario eseguire i metodi asincroni utilizzando Task.Run() o qualsiasi altro mezzo speciale, basta chiamarli. E nel tuo caso, questo è esattamente ciò che sta causando il problema.

funzione data come questa:

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

chiamata in questo modo da un certo metodo di esecuzione sul thread dell'interfaccia utente, come un gestore di eventi:

f(); 

funziona esattamente come dovrebbe. Esegue la prima iterazione del ciclo e quindi ritorna. La successiva iterazione viene eseguita dopo 500 ms (o più, se il thread dell'interfaccia utente è occupato) sul thread dell'interfaccia utente.

D'altra parte, se lo si chiama in questo modo:

Task.Run(addNames); 

non funziona correttamente. La ragione di ciò è che i metodi async provano a continuare nello stesso contesto in cui erano stati avviati (a meno che non specifichi esplicitamente il contrario). La prima versione è stata avviata sul thread dell'interfaccia utente, quindi è proseguita sul thread dell'interfaccia utente. La seconda versione è iniziata su un thread ThreadPool (grazie a Task.Run()) e ha continuato anche lì. Ecco perché ha causato il tuo errore.

Tutto ciò viene eseguito utilizzando SynchronizationContext, se presente.

3

È stato creato un ObservableCollection sul thread principale dell'interfaccia utente e si sta tentando di aggiornarlo su un thread in background asincrono, che non è possibile eseguire in WPF.

In alternativa, ottenere i risultati da un thread in background, quindi aggiungerli a ObservableCollection sul thread principale dell'interfaccia utente.

Di solito il mio codice per l'aggiornamento di un ObservableCollection su un thread in background sarà simile a questa:

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

Se è vero che non si è in grado di aggiornare un ObservableCollection da un secondo thread, è possibile per creare una collezione osservabile asincrona. Ciò consente di aggiornare la raccolta all'interno delle attività o sui thread in cui la raccolta non è stata creata.

Vorrei postare l'esempio, ma ho trovato le informazioni qui. È piuttosto lucido, e ho trovato che sia un esempio molto utile.

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

Problemi correlati