Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM Sync Collections

Tags:

c#

mvvm

wpf

Is there a standardized way to sync a collection of Model objects with a collection of matching ModelView objects in C# and WPF? I'm looking for some kind of class that would keep the following two collections synced up assuming I only have a few apples and I can keep them all in memory.

Another way to say it, I want to make sure if I add an Apple to the Apples collection I would like to have an AppleModelView added to the AppleModelViews collection. I could write my own by listening to each collections' CollectionChanged event. This seems like a common scenario that someone smarter than me has defined "the right way" to do it.

public class BasketModel {     public ObservableCollection<Apple> Apples { get; } }  public class BasketModelView {     public ObservableCollection<AppleModelView> AppleModelViews { get; } } 
like image 857
Jake Pearson Avatar asked Aug 10 '09 19:08

Jake Pearson


1 Answers

I use lazily constructed, auto-updating collections:

public class BasketModelView {     private readonly Lazy<ObservableCollection<AppleModelView>> _appleViews;      public BasketModelView(BasketModel basket)     {         Func<AppleModel, AppleModelView> viewModelCreator = model => new AppleModelView(model);         Func<ObservableCollection<AppleModelView>> collectionCreator =             () => new ObservableViewModelCollection<AppleModelView, AppleModel>(basket.Apples, viewModelCreator);          _appleViews = new Lazy<ObservableCollection<AppleModelView>>(collectionCreator);     }      public ObservableCollection<AppleModelView> Apples     {         get         {             return _appleViews.Value;         }     } } 

Using the following ObservableViewModelCollection<TViewModel, TModel>:

namespace Client.UI {     using System;     using System.Collections.Generic;     using System.Collections.ObjectModel;     using System.Collections.Specialized;     using System.Diagnostics.Contracts;     using System.Linq;      public class ObservableViewModelCollection<TViewModel, TModel> : ObservableCollection<TViewModel>     {         private readonly ObservableCollection<TModel> _source;         private readonly Func<TModel, TViewModel> _viewModelFactory;          public ObservableViewModelCollection(ObservableCollection<TModel> source, Func<TModel, TViewModel> viewModelFactory)             : base(source.Select(model => viewModelFactory(model)))         {             Contract.Requires(source != null);             Contract.Requires(viewModelFactory != null);              this._source = source;             this._viewModelFactory = viewModelFactory;             this._source.CollectionChanged += OnSourceCollectionChanged;         }          protected virtual TViewModel CreateViewModel(TModel model)         {             return _viewModelFactory(model);         }          private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)         {             switch (e.Action)             {             case NotifyCollectionChangedAction.Add:                 for (int i = 0; i < e.NewItems.Count; i++)                 {                     this.Insert(e.NewStartingIndex + i, CreateViewModel((TModel)e.NewItems[i]));                 }                 break;              case NotifyCollectionChangedAction.Move:                 if (e.OldItems.Count == 1)                 {                     this.Move(e.OldStartingIndex, e.NewStartingIndex);                 }                 else                 {                     List<TViewModel> items = this.Skip(e.OldStartingIndex).Take(e.OldItems.Count).ToList();                     for (int i = 0; i < e.OldItems.Count; i++)                         this.RemoveAt(e.OldStartingIndex);                      for (int i = 0; i < items.Count; i++)                         this.Insert(e.NewStartingIndex + i, items[i]);                 }                 break;              case NotifyCollectionChangedAction.Remove:                 for (int i = 0; i < e.OldItems.Count; i++)                     this.RemoveAt(e.OldStartingIndex);                 break;              case NotifyCollectionChangedAction.Replace:                 // remove                 for (int i = 0; i < e.OldItems.Count; i++)                     this.RemoveAt(e.OldStartingIndex);                  // add                 goto case NotifyCollectionChangedAction.Add;              case NotifyCollectionChangedAction.Reset:                 Clear();                 for (int i = 0; i < e.NewItems.Count; i++)                     this.Add(CreateViewModel((TModel)e.NewItems[i]));                 break;              default:                 break;             }         }     } } 
like image 99
Sam Harwell Avatar answered Sep 23 '22 12:09

Sam Harwell