You can use something like this to keep your ObservableCollections synchronised between the model and view model:
/// <summary>
/// Keeps one collection synchronised with another.
/// </summary>
/// <typeparam name="Source">The type of the source items.</typeparam>
/// <typeparam name="Destination">The type of the destination items.</typeparam>
public class CollectionSync<Source, Destination>
{
private readonly Func<Source, Destination> _destItemFactory;
private readonly Action<Destination> _destItemRemover;
private readonly IList<Destination> _destList;
private readonly IList<Source> _sourceList;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionSync<Source, Destination>"/> class.
/// </summary>
/// <param name="sourceList">The source list.</param>
/// <param name="destList">The destination list.</param>
/// <param name="destItemFactory">Factory method which creates a Destination for a given Source.</param>
/// <param name="destItemRemover">Method called when a Destination is removed.</param>
public CollectionSync(IList<Source> sourceList,
IList<Destination> destList,
Func<Source, Destination> destItemFactory,
Action<Destination> destItemRemover)
{
_destItemFactory = destItemFactory;
_destItemRemover = destItemRemover;
_sourceList = sourceList;
_destList = destList;
((INotifyCollectionChanged) _sourceList).CollectionChanged += SourceCollection_CollectionChanged;
PopulateWithAllItems();
}
private void PopulateWithAllItems()
{
foreach (Source sourceItem in _sourceList)
_destList.Add(_destItemFactory(sourceItem));
}
private void SourceCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
OnItemsAdded(args.NewStartingIndex, args.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
OnItemsRemoved(args.OldStartingIndex, args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
OnItemsReset();
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
throw new NotImplementedException();
}
}
private void OnItemsReset()
{
_destList.Clear();
PopulateWithAllItems();
}
private void OnItemsRemoved(int index, ICollection items)
{
int itemCount = items.Count;
for (int i = 0; i < itemCount; i++)
{
Destination removed = _destList[index];
_destList.RemoveAt(index);
if (_destItemRemover != null)
_destItemRemover(removed);
}
}
private void OnItemsAdded(int index, IList items)
{
int itemIndex = index;
foreach (Source item in items)
{
// Add to Items collection
_destList.Insert(itemIndex, _destItemFactory(item));
itemIndex++;
}
}
}
Taking your Order/OrderDetails example, in your Order view model you would hook up the two ObservableCollections like this:
_modelToViewModelSync = new CollectionSync<IOrderDetail, OrderDetailViewModel>(
orderDetailModels, // the list of your order details models
OrderDetails, // the list of order details view models exposed by the Order view model
x => new OrderDetailViewModel(x), // factory method to create a view model
null); // do something here if you care when your view models are removed