2

I've read some questions on stackoverflow targeting this issue but found none actually providing a way on how to do it properly. I just don't feel right about my implementation.

Given the following situation: I got a list of playlists (music playlists, for example) that do contain tracks.

class PlaylistModel
-> string Name // Name of the playlist
-> List<TrackModel> Tracks // tracks contained by the playlist

class TrackModel
-> string Name

Given this scheme, how would I map this relation if my ViewModel shows playlists and the associated tracks as a TreeView that may change during user interaction?

I already thought about the relationship and ended up using something like this:

public class PlaylistViewModel : ViewModelBase
{
    private PlaylistModel _Playlist;
    public PlaylistModel Playlist
    {
        get { return _Playlist; }
        set
        {
            if (_Playlist == value) return;
            _Playlist = value;
            NotifyOfPropertyChange(() => Playlist);
        }
    }

    public ObservableCollection<TrackViewModel> Tracks
    {
        get { return new ObservableCollection<TrackViewModel>(Playlist.Tracks.Select(track => new TrackViewModel(track))); }
        set
        {
            var tracksCollection = new ObservableCollection<TrackViewModel>(Playlist.Tracks.Select(track => new TrackViewModel(track)));
            if (tracksCollection == value) return;
            Playlist.Tracks = new List<TrackModel>(value.Select(trackViewModel => trackViewModel.Track));
            NotifyOfPropertyChange(() => Tracks);
        }
    }

    public string Name
    {
        get { return Playlist.Name; }
        set
        {
            if (Playlist.Name == value) return;
            Playlist.Name = value;
            NotifyOfPropertyChange(() => Name);
        }
    }
}

(just included the essential code part)

You might have noticed the casting around the _Playlist.Tracks property which is of type TrackModel. That's the issue here. I just could have implemented INotifyPropertyChanged in the TrackModel class but I also don't feel right about this one since it simply isn't my UI interaction layer.

0x8BADF00D
  • 962
  • 1
  • 8
  • 18

1 Answers1

2

See this discussion for implementing INotifyPropertyChanged in the model, viewmodel or both.

If you have a workflow that matches the following

  1. Load playlist from server
  2. User modifies playlist
  3. User either saves the playlist or discards changes

you may also consider keeping your models sans INotifyPropertyChanged and utilize AutoMapper to convert from the model to the viewmodel (Load) and the viewmodel to the model (save).

Another post on the matter.

Community
  • 1
  • 1
Jason Fry
  • 1,204
  • 12
  • 24
  • Something that implements INotifyPropertyChanged is really a ViewModel because the only thing that really subscribes to property change notifications are view bindings. There isn't necessarily anything wrong with an app with just a ViewModel (and no Model)--the "model" can be built-in types like collections, etc. If you find you need to add a lot of non-view specific logic, it's better to separate the model from the view model. – Peter Ritchie Oct 07 '14 at 18:47
  • Hey and thanks for your answer. Stumbled across this comment in the discussion: `What do you do if a property changes in the model? You need to get it to the view-model somehow. Honest question, I'm dealing with this conundrum right now.` According to the up-votes, this seems to be commonly used? Basically this is what I'm facing in this exact example. – 0x8BADF00D Oct 07 '14 at 18:48
  • @PeterRitchie If I have a class that is represents an entity in my domain, populated with data received from a database and contains business logic that acts on that data that class is a model. If it then implements INotifyPropertyChanged it's still a model. If the same class is then tied directly to the view's DataContext, then I believe that it is a model and a viewmodel. – Jason Fry Oct 07 '14 at 19:01
  • @JasonFry I disagree because binding is a view concern, not a model concern. Remember that a model should be independent on the view and should have no dependencies on anything view related. It's at a lower level and should not take on dependencies above it. – Peter Ritchie Oct 07 '14 at 19:04
  • @PeterRitchie We both agree that separating the model from concerns of the view is preferred (see my original answer), but a model that implements INotifyPropertyChanged is not necessarily dependent on the view. After all, INotifyPropertyChanged is used in places other than WPF views/viewmodels. – Jason Fry Oct 07 '14 at 19:15
  • @Mostey Are you refreshing your model while the user is editing a playlist? If so, implementing INotifyPropertyChanged in your model may be appropriate or the code that is refreshing the model could send updates along to the viewmodel. – Jason Fry Oct 07 '14 at 19:19
  • Anything that uses `INotifyPropertyChanged`, in my opinion, is a type of view. Not all "views" are visual. If you look at various recommended practices around messaging, domain objects, event sourcing, etc. You quickly realize that change is state is *much* better handled in other more domain-logic/business-logic ways. Thinking that models can use INPC takes you take a very coupled road... e.g. imagine your model updating in real-time in a background thread and having to deal with marshalling that noise back to the UI thread... – Peter Ritchie Oct 07 '14 at 19:19
  • The view model updating the model *and* updating the view via INPC is a much better way of doing things. i.e. a separate model and a separate view model. – Peter Ritchie Oct 07 '14 at 19:20
  • @JasonFry The user may also edit tracks. He could edit the name, for example. I'd need to implement `INotifyPropertyChanged` in the model since the changes won't get reflected to the View otherwise. That is what drives me crazy and which I wanted to prevent with another ViewModel. But since that is tied to casting (and also new instances), the solution in my example in the question seems to be very bad. – 0x8BADF00D Oct 07 '14 at 19:22
  • @Mostey Are these changes being persisted to a server? Will other users be able to modify the same playlists/tracks? Or is the entire model kept on the client? – Jason Fry Oct 07 '14 at 19:28
  • @JasonFry Will be kept locally. I'm curious, why are you asking? – 0x8BADF00D Oct 07 '14 at 19:32