I have a problem understanding how to build view models based on the following models
(I simplified the models to be clearer)
public class Hit
{
publi
One solution I've been considering, although I'm not sure if it would work perfectly in practice, is to use converters to create a viewmodel around your model.
So in your case, you could bind Tracks
directly to (as an example) a listbox, with a converter that creates a new TrackViewModel
from the Track. All your control would ever see would be a TrackViewModel
object, and all your models will ever see is other models.
I'm not sure about the dynamic updating of this idea though, I've not tried it out yet.
I ended up using part of the solution that Joe White suggested, in a slighty differ manner
The solution was to just leave the models as they were at the beginning, and attaching to the collections an eventhandler for CollectionChanged of the inner collections, for example, the PatternViewModel would be:
public class PatternViewModel : ISerializable
{
public Pattern Pattern { get; set; }
public ObservableCollection<TrackViewModel> Tracks { get; set; }
public PatternViewModel(string name)
{
Pattern = new Pattern(name);
Tracks = new ObservableCollection<TrackViewModel>();
Pattern.Tracks.CollectionChanged += new NotifyCollectionChangedEventHandler(Tracks_CollectionChanged);
}
void Tracks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Track track in e.NewItems)
{
var position = Pattern.Tracks.IndexOf((Track) e.NewItems[0]);
Tracks.Insert(position,new TrackViewModel(track, this));
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Track track in e.OldItems)
Tracks.Remove(Tracks.First(t => t.Track == track));
break;
case NotifyCollectionChangedAction.Move:
for (int k = 0; k < e.NewItems.Count; k++)
{
var oldPosition = Tracks.IndexOf(Tracks.First(t => t.Track == e.OldItems[k]));
var newPosition = Pattern.Tracks.IndexOf((Track) e.NewItems[k]);
Tracks.Move(oldPosition, newPosition);
}
break;
}
}
}
So i can attach the new Color/Style/Command on the view models to keep my base models clean
And whenever I add/remove/move items in the base models collection, the view models collections remain in sync with each other
Luckily I don't have to manage lots of object in my application, so duplicated data and performance won't be a problem
I don't like it too much, but it works well, and it's not a huge amount of work, just an event handler for the view model that contains others view model collections (in my case, one for PatternViewModel to sync TrackViewModels and another on TrackViewModel to manage HitViewModels)
Still interested in your thoughs or better ideas =)
One thing I've done, with some success, is to move the ObservableCollection out of the model. Here's my general pattern:
IEnumerable<TModel>
that gives read-only access to the collection. Use a plain old List<TModel>
, not an ObservableCollection, as the backing collection.ObservableCollection<TNestedViewModel>
.This makes for a lot of duplicate code for each different ViewModel, but it's the best I've been able to come up with. It does at least scale based on the complexity you need -- if you have a collection that's add-only, you don't have to write much code; if you have a collection that supports arbitrary reordering, inserts, sorting, etc., it's much more work.
I think I had the same problem and if you do it like "PatternViewModel with an ObservableCollection<TrackViewModel>" you also get a massive impact on your performance because you start duplicating data.
My approach was to build - for your expample - a PatternViewModel with a ObservableCollection<Track>. It's no contradiction to MVVM because the view is bound to the collection.
This way you may avoid the duplication of the relationships.