Bi-directional view model syncing with "live" collections and properties

Personally, I use an ObservableCollection in my model and my viewmodel.

class Model
{
    public ObservableCollection<Foo> Foos;
}

class ViewModel
{
    public Model Model;
    public ObservableCollection<FooView> Foos;

    public ViewModel()
    {
        Model.Foos.CollectionChanged += OnModelFoosCollection_CollectionChanged;
    }

    void OnModelFoosCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
       Foo re;

       switch (e.Action)
       {
          case NotifyCollectionChangedAction.Add:
             re = e.NewItems[0] as Foo;
             if (re != null)
                AddFoo(re);  //For other logic that may need to be applied
             break;
          case NotifyCollectionChangedAction.Remove:
             re = e.OldItems[0] as Foo;
             if (re != null)
                RemoveFoo(re); 
             break;
          case NotifyCollectionChangedAction.Reset:
             Foos.Clear();
             /* I have an AddRange in an ObservableCollection-derived class
                You could do Model.Foo.ForEach(ree => AddFoo(ree));
             */
             var converter = 
                from ree in Model.Foo
                select new FooView(ree);
             Reports.AddRange(converter); 
             break;
          default:
             //exercise for the reader :)
             s_ILog.Error("OnModelFoosCollection_CollectionChangedDid not deal with " + e.Action.ToString()); 
             break;
       }
    }   

    void AddFoo(Foo f)
    {
        Foos.Add(new FooView(f));
    }

    void RemoveFoo(Foo f)
    {
       var match = from f in Foos
          where f.Model == f  //if you have a unique id, that might be a faster comparison
          select f;
       if(match.Any())
          Foos.Remove(match.First());
    }
}

Now, when you remove something from the Model's Foo collection, it will automatically remove the corresponding FooView. This corresponds with how I think about this sort of thing. If I want to delete something, the Model is where is really needs to be deleted.

It feels like a lot of code, but it isn't that much, really. I'm sure one could build a generic version of this, but IMO you always end up wanting custom logic dealing with addition/removal of elements.


The only situation, when you might need the two-way synchronization, is when the control that you use to visualize your collection of VMs does not let you know of user's intention to create or remove an item. I.e. the control deals directly with your collection of VMs and the ONLY way you know the item has been added/removed, is by monitoring the collection of VMs. If this is not the case then you can implement one way sync and add/remove items directly on model's collection.

EDIT: Take for example WPF DataGrid control bound to observable collection of ItemViewModels. If its CanUserAddRows property is set to true and the user starts typing in the empty row at the bottom, the DataGrid will use default constructor of your ItemViewModel to create a loose item and then will add it to the collection. There is no indication from DG that it wants to add an item the collection.c I can't think of any other control that is complicated enough to be able to add items to collection on its own.
The opposite scenario is when you have ListView bound to your collection and a command which indicates user's intention to add new item - then in command handler you simply add new item to DataModel and let the one-way sync do the rest of the job. In this case ListView is not able to add to the collection it presents.

As to the sync process itself, look at Bindable LINQ project - it can minimize the amount code and improve readability. For example the code Tom posted will translate into something like this:

class ViewModel
{
  public Model Model;
  public ObservableCollection<FooView> Foos;
  public ViewModel()
  {
    Foos = from foo in Model.Foos.AsBindable()
           select new FooView(foo);
  }
}

EDIT 2: After using B-LINQ for some time now I should say that you might have performance issues with it. I have used it to synchronize relatively big collections (hundreds of elements) collections with tens of elements being added and removed every second and I had to give it up and implement synchronization the way Tom had suggested.
I still use B-LINQ though in those parts of the project where collections are small and performance is not an issue.


I too am struggling with the bi-directional sync of two collections for use with WPF via MVVM. I blogged MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model? (Part 1) and MVVM: To Wrap or Not to Wrap? Should ViewModels wrap collections too? (Part 2) regarding the question, including some sample code that shows a two way sync. However, as noted in the posts, the implementation is not ideal. I would qualify it as a proof of concept.

I like the BLINQ, CLINQ, and Obtics frameworks that Alex_P posted about. These are a very nice way to get one side of the sync behvaior. Maybe the other side (from VM to Model) can be implemented via an alternate path? I just posted part 3 on my blog that discusses some of this.

From what I can see, bi-directional via BLINQ and CLINQ is not supported in cases where the LINQ statement projects the data to a new structure.

However, it does look like CLINQ may support Bi-Directional syncing in cases where the LINQ query returns the same datatype as the underlying collection. This is more of a filtering scenario, which doesn't match the use case of a ViewModel wrapping the data in the Model.