2011-01-21 14 views
5

da due giorni sto cercando di risolvere il seguente problema: Ho un controllo WPF in cui un WrapPanel è associato a una ObservableCollection. Un'azione cambia il contenuto di ObservableCollection. Il contenuto è caricato in un BackgroundWorker. Subito dopo l'azione che ha causato la modifica del contenuto, il nuovo contenuto è necessario in un ciclo foreach. Il problema è che il caricamento del contenuto è lento, quindi è necessario un po 'per essere pronto.Come aggiornare ObservableCollection dall'interno di un BackgroundWorker usando MVVM?

Il mio primo tentativo è stato attendere il backgroundworker fino a quando la proprietà IsBusy è impostata su false. Ma la proprietà IsBusy non è mai cambiata durante l'attesa! Il secondo tentativo era di provare a manipolare ObservableCollection direttamente da BackgroundWorker. Ovviamente non ha successo perché ObservableCollection si trova in un altro thread rispetto a BackgroundWorker.

Ho letto davvero molto su come manipolare il contenuto in cross-thread-wide. Ma nessuno di loro ha funzionato. Provato soluzioni con Dispatcher, "ThreadSafeObservableCollection", .....

Qualcuno potrebbe dirmi come posso risolvere questo problema? C'è un modo semplice per modificare il contenuto del thread dell'interfaccia utente all'interno di un altro thread? O come posso aspettare correttamente che BackgroundWorker sia finito?

EDIT: Ma come posso aspettare il BackgroundWorker per ottenere finito ???

risposta

4

Spingere lo ObservableCollection.Add nel dispatcher del thread dell'interfaccia utente dovrebbe funzionare.

App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item))); 
+0

questo non ha aiutato in un risparmio di tempo, si corre ma sullo stesso thread! –

1

Il BackGroundWorker genera un evento al termine. Quello che ho fatto in una situazione simile è:

Ho una lista che non è un observablecollecion

  • Set abilitato = false nella mia finestra e visualizzare un filatore
  • Inizio sfondo lavoratore
  • in DoWork compilo l'elenco
  • in evento RunWorkerCompleted copio il contenuto dell'elenco nella mia raccolta di osservazioni e abilito roba nascondi spinner

quindi tutte le interazioni con la raccolta sono sullo stesso thread - la copia di solito non è la parte più costosa.

2

È possibile aggiornare la raccolta nel gestore eventi BackgroundWorker.RunWorkerCompleted. Viene eseguito nello stesso contesto di sincronizzazione che è stato avviato, solitamente un thread dell'interfaccia utente, in modo da poter utilizzare in tutta sicurezza qualsiasi materiale correlato all'interfaccia utente.

8

BackgroundWorker può aiutarti in due modi.

Per aggiornare la raccolta mentre BGWorker è in esecuzione, utilizzare l'evento ProgressChanged. Il nome di questo evento è fuorviante - mentre tu puoi aggiornare l'avanzamento di un'attività, puoi usarlo effettivamente per tutto ciò che deve essere fatto nel thread dell'interfaccia utente (chiamata) passando un oggetto usando la proprietà UserState del ProgressChangedEventArgs.

Il BGWorker ha anche un evento al termine.Anche in questo caso, è possibile passare qualsiasi informazione ad essa desiderata nella proprietà Result di RunWorkerCompletedEventArgs nell'evento RunWorkerCompleted.

Il seguente codice è da another thread che ho risposto su BackgroundWorker:

BackgroundWorker bgWorker = new BackgroundWorker(); 
ObservableCollection<int> mNumbers = new ObservableCollection<int>(); 

public Window1() 
{ 
    InitializeComponent(); 
    bgWorker.DoWork += 
     new DoWorkEventHandler(bgWorker_DoWork); 
    bgWorker.ProgressChanged += 
     new ProgressChangedEventHandler(bgWorker_ProgressChanged); 
    bgWorker.RunWorkerCompleted += 
     new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted); 
    bgWorker.WorkerReportsProgress = true; 

    btnGenerateNumbers.Click += (s, e) => UpdateNumbers(); 

    this.DataContext = this; 
} 

void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    progress.Visibility = Visibility.Collapsed; 
    lstItems.Opacity = 1d; 
    btnGenerateNumbers.IsEnabled = true; 
} 

void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    List<int> numbers = (List<int>)e.UserState; 
    foreach (int number in numbers) 
    { 
     mNumbers.Add(number); 
    } 

    progress.Value = e.ProgressPercentage; 
} 

void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    Random rnd = new Random(); 
    List<int> numbers = new List<int>(10); 

    for (int i = 1; i <= 100; i++) 
    { 
     // Add a random number 
     numbers.Add(rnd.Next());    

     // Sleep from 1/8 of a second to 1 second 
     Thread.Sleep(rnd.Next(125, 1000)); 

     // Every 10 iterations, report progress 
     if ((i % 10) == 0) 
     { 
      bgWorker.ReportProgress(i, numbers.ToList<int>()); 
      numbers.Clear(); 
     } 
    } 
} 

public ObservableCollection<int> NumberItems 
{ 
    get { return mNumbers; } 
} 

private void UpdateNumbers() 
{ 
    btnGenerateNumbers.IsEnabled = false; 
    mNumbers.Clear(); 
    progress.Value = 0; 
    progress.Visibility = Visibility.Visible; 
    lstItems.Opacity = 0.5; 

    bgWorker.RunWorkerAsync(); 
} 
Problemi correlati