2013-06-12 10 views
5

Sto riscontrando un problema con il recupero dati da db e la visualizzazione in UI in modo asincrono. Sto usando luce MVVM, quando si fa clic sul pulsante, azione viene attivata in ViewModel:Aggiornamento dell'interfaccia utente asincrona da ViewModel in WPF

private void SearchQuery(string query) 
    { 
     _redisModel.GetFriendsListAsync(query); 
    } 

Ad un certo punto GetFriendsListCompleted viene chiamato dal thread in background notifing ViewModel che lavoro è fatto. A questo punto ho bisogno di aggiornare ListBox ItemSource. Ma quando provo ad aggiornare è ottengo “Il thread chiamante non può accedere a questo oggetto perché un thread diverso lo possiede” ho cercato Dispatcher.CurrentDispatcher.Invoke(), App.Current.Dispatcher.Invoke() e magie diverse, ma ancora non funziona.

Ho provato a fornire a UI il dispatcher a ViewModel e quindi a chiamare da lì - non ha funzionato.

private string filterText = string.Empty; 
    public string FilterText 
    { 
     get { return filterText; } 
     set 
     { 
      filterText = value; 
      this.RaisePropertyChanged(() => this.FilterText); 

      this.FriendsList.View.Refresh(); // Here where exception is happening. 
     } 
    } 

Ho provato a cambiare questa riga per

Dispatcher.Invoke (DispatcherPriority.Normal, nuova azione ( () => this.FriendsList.View.Refresh())); - ancora lo stesso.

Sto usando Telerik ListBox per visualizzare gli elementi. FriendList è CollectionViewSource (http://www.telerik.com/help/wpf/radlistbox-overview.html). Funziona quando uso l'esempio Telerik dagli esempi di controllo WPF. I problemi iniziano a verificarsi quando utilizzo i miei metodi asincroni. Il tipo di visualizzazione è System.ComponentModel.ICollectionView è utilizzato per il filtro e il raggruppamento.

Ho anche provato ad assegnare ObservableCollection alla proprietà Items del ListBox e non funziona neanche.

un po 'più dettagli su come funziona _redisModel.GetFriendsListAsync: Alla fine (dopo tutta la catena di chiamate) si finisce qui:

public GetAsyncResult(Func<T> workToBeDone, Action<IAsyncResult> cbMethod, Object state) 
{ 
    _cbMethod = cbMethod; 
    _state = state; 
    QueueWorkOnThreadPool(workToBeDone); 
} 

ThreadPool.QueueUserWorkItem(state => 
{ 
    try 
    { 
    _result = workToBeDone(); 
    } 
    catch (Exception ex) 
    { 
     _exception = ex; 
    } 
    finally 
    { 
    UpdateStatusToComplete(); //1 and 2 
    NotifyCallbackWhenAvailable(); //3 callback invocation 
    } 
}); 

In viewmodel ho metodo:

private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e) 
    { 
     if (!e.HasError) 
     { 
      var curr = e.Results; 
      if (curr != null) 
      { 
       this.FriendsList= new CollectionViewSource(); 

       this.FriendsList.Source = list; 
       this.FriendsList.Filter += this.FriendFilter; 
       FilterText = ""; 

       Dispatcher.Invoke(DispatcherPriority.Normal, new Action(

         () => this.FriendsList.View.Refresh())); 
      } 
    } 

Qualcuno può aiutarmi per favore con questo? Grazie

+0

@ Robert Kruszewski – LucasSeveryn

+0

modificare la tua domanda e aggiungere un po contesto alla tua domanda. Qual è il tipo di 'FriendsList'. Qual è la proprietà 'View' a cui stai accedendo dalla VM? – Viv

risposta

4

Si sta creando CollectionViewSource in un thread e si aggiorna in un altro thread (thread del dispatcher). Aggiorna il tuo GetFriendsListCompleted a

private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e) 
{ 
    if (!e.HasError) 
    { 
     var curr = e.Results; 
     if (curr != null) 
     { 
      Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
        () => { 
        this.FriendsList= new CollectionViewSource(); 
        this.FriendsList.Source = list; 
        this.FriendsList.Filter += this.FriendFilter; 
        FilterText = ""; 
        this.FriendsList.View.Refresh(); 
        })); 
     } 
    } 
} 
+0

Ottimo! Ha funzionato! Grazie mille. Ma attualmente sto passando App.Current.Dispatcher in ViewModel all'interno di CodeBehind. C'è un approccio più elegante? – Andy

+0

@Andy risposta per favore Check accettati [questa domanda] (http://stackoverflow.com/questions/2354438/how-to-pass-the-ui-dispatcher-to-the-viewmodel) – sthotakura

+0

ho provato ed è doesn' lavoro. Fallisce dopo aver aggiunto un nuovo parametro al costruttore ViewModel. MEF non riusciva a trovare. È strano perché ho già un parametro - il mio componente DAL e funziona. Penso che abbia qualcosa a che fare con la classe WpfContext dichiarata nell'interfaccia utente e l'ordine di inizializzazione. – Andy

0

Non hai mostrato alcun codice in esecuzione sul thread in background al completamento, ma suppongo che in esso stai creando un oggetto di raccolta che stai cercando di assegnare al tuo CollectionView. Quando il CV tenta di aggiornare (sul thread dell'interfaccia utente) dalla chiamata di aggiornamento, tenta quindi di utilizzare la raccolta di proprietà dell'altro thread.

Se si include il codice pertinente, sarebbe più facile dirlo con certezza.

+0

La tua ipotesi è abbastanza accurata. Ho aggiornato le domande con più codice. – Andy

Problemi correlati