2010-09-02 7 views
9

Ok, ich habe kürzlich einen Hintergrund-Worker zum Speichern und Laden von Daten implementiert.Aktualisieren einer ObservableCollection mit einem Hintergrund-Worker in MVVM

Dies ist jedoch immer auf einem Sparbefehl zu arbeiten hat sich als schwierig erwiesen.

Grundsätzlich mein Befehl speichern generiert ein Ereignis, das eine Sammlung Ansicht Modell benachrichtigt, dass ein Element hinzugefügt wurde, und dass das Element in seine eigenen ObservableCollection hinzugefügt werden soll.

An diesem Punkt habe ich die übliche Ausnahme sagen, dass ich nicht eine ICollection auf einem anderen Thread aktualisieren kann. Ich habe versucht, einen neuen Listentyp zu erstellen, der Dispatcher.Invoke aufruft, jedoch generiert dies immer noch die gleiche Ausnahme.

Ich habe mich gefragt, ob jemand irgendwelche Vorschläge, wie dies am besten zu bewältigen?

Also zur Zeit habe ich eine Klasse, die von ObservableCollection Erbt:

public class ThreadSafeObservableCollection<T> : ObservableCollection<T> 
{ 
    public ThreadSafeObservableCollection(List<T> collection) 
     : base(collection) 
    { 
     dispatcher = Dispatcher.CurrentDispatcher; 
     rwLock = new ReaderWriterLock(); 
    } 

    protected override void InsertItem(int index, T item) 
    { 
     if (dispatcher.CheckAccess()) 
     { 
      if (index > this.Count) 
       return; 
      LockCookie c = rwLock.UpgradeToWriterLock(-1); 
      base.InsertItem(index, item); 
      rwLock.DowngradeFromWriterLock(ref c); 
     } 
     else 
     { 
      object[] obj = new object[] { index, item }; 
      dispatcher.Invoke(
       DispatcherPriority.Send, 
       (SendOrPostCallback)delegate { InsertItemImpl(obj); }, 
       obj); 
     } 
    } 

ich dann eine Ansicht Modellklasse haben, die einen Hintergrund Arbeiter hat, die das Speichern ausführt.

Sobald das Speichern abgeschlossen ist, wird ein Ereignis in einer anderen Ansicht Modell feuert seine Liste zu aktualisieren.

protected override void OnObjectAddedToRepository(object sender, ObjectEventArgs<cdAdministrators> e) 
    { 
     Dispatcher x = Dispatcher.CurrentDispatcher; 
     var viewModel = new AdministratorViewModel(e.EventObject, DataAccess); 
     viewModel.RecentlyAdded = true; 
     viewModel.ItemSelected += this.OnItemSelected; 
     this.AllViewModels.Add(viewModel); 
     RecentlyAddedViewModel = viewModel; 

     OnPropertyChanged(null); 
    } 

Beide Listen werden von einem separaten Hintergrund-Worker-Thread erstellt.

Antwort

7

Wo Sie Code haben, der das Objekt zur beobachtbaren Sammlung hinzufügt (vermutlich im View-Modell), wickeln Sie diesen Add Aufruf in einen Dispatcher.BeginInvoke Aufruf ein.

Zugegeben, den das View-Modell bedeutet, muss über den Dispatcher wissen, die dann umständlich zu testen, wird ... zum Glück ist es nicht zu hart, um Ihre eigene IDispatcher Schnittstelle einzuführen und Dependency Injection in gewohnter Weise zu nutzen.

+0

Hallo Jon, Vielen Dank für Ihre Antwort, Ich habe bereits ein Sammelobjekt, das auf InsertItem von ObserveableCollection Erbt dies tut Dispatcher.CheckAccess wenn falsche Dispather.BeginInvoke nicht dann ist dies jedoch immer noch nicht funktioniert ?? – jpgooner

+0

@jpgooner: Sind Sie sicher, dass der Code tatsächlich verwendet wird? Können Sie sich ein kurzes, aber vollständiges Beispiel vorstellen, das das Problem aufzeigt? –

+0

Ich habe weiter oben hinzugefügt, lassen Sie mich wissen, wenn Sie mehr brauchen, habe ich noch neu bin – jpgooner

3

Wie wäre es damit?

public class ThreadSafeObservableCollection<T> : ObservableCollection<T> 
{ 
    private SynchronizationContext SynchronizationContext; 

    public ThreadSafeObservableCollection() 
    { 
     SynchronizationContext = SynchronizationContext.Current; 

     // current synchronization context will be null if we're not in UI Thread 
     if (SynchronizationContext == null) 
      throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con        structor."); 
    } 

    public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext) 
    { 
     if (synchronizationContext == null) 
      throw new ArgumentNullException("synchronizationContext"); 

     this.SynchronizationContext = synchronizationContext; 
    } 

    protected override void ClearItems() 
    { 
     this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null); 
    } 

    protected override void InsertItem(int index, T item) 
    { 
     this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null); 
    } 

    protected override void RemoveItem(int index) 
    { 
     this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null); 
    } 

    protected override void SetItem(int index, T item) 
    { 
     this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null); 
    } 

    protected override void MoveItem(int oldIndex, int newIndex) 
    { 
     this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null); 
    } 
} 
2

fand ich ein blog post, die den Dispatcher verwenden alle die Methoden des ObeservableCollection zu verwalten. Hier ist ein Snip-It des Codes, siehe post für die gesamte Klasse.

public class DispatchingObservableCollection<T> : ObservableCollection<T> 
{ 
    /// <summary> 
    /// The default constructor of the ObservableCollection 
    /// </summary> 
    public DispatchingObservableCollection() 
    { 
     //Assign the current Dispatcher (owner of the collection) 
     _currentDispatcher = Dispatcher.CurrentDispatcher; 
    } 

    private readonly Dispatcher _currentDispatcher; 

    /// <summary> 
    /// Executes this action in the right thread 
    /// </summary> 
    ///<param name="action">The action which should be executed</param> 
    private void DoDispatchedAction(Action action) 
    { 
     if (_currentDispatcher.CheckAccess()) 
      action(); 
     else 
      _currentDispatcher.Invoke(DispatcherPriority.DataBind, action); 
    } 

    /// <summary> 
    /// Clears all items 
    /// </summary> 
    protected override void ClearItems() 
    { 
     DoDispatchedAction(() => base.ClearItems()); 
    } 

    /// <summary> 
    /// Inserts a item at the specified index 
    /// </summary> 
    ///<param name="index">The index where the item should be inserted</param> 
    ///<param name="item">The item which should be inserted</param> 
    protected override void InsertItem(int index, T item) 
    { 
     DoDispatchedAction(() => base.InsertItem(index, item)); 
    } 
+0

Gutes Beispiel! Nur eine kleine Korrektur: protected override void InsertItem (int index, T-Element) {DoDispatchedAction (() => BaseInsertItem (index, item)); } ist in der Tat geschützt override void InsertItem (int index, T item) {DoDispatchedAction (() => base.InsertItem (index, item)); } –

+0

Danke. Ich bin mir nicht sicher, warum ich explizit eine Methode erstellt und eine anonyme Methode für das zweite Beispiel verwendet habe. Ich habe sie aufgeräumt. – chilltemp

Verwandte Themen