5

When utilising the MVVM pattern I am coming into some trouble when the Model objects become complex, that is, when they contain Properties which are non-primitive / not built-in. In my particular instance I have a ModelA which contains a collection of ModelB objects which themselves contains a collection of ModelC objects:

class ModelA
{
    public string Name { get; set; }
    public OberservableCollection<ModelB> Bs { get; set; }
}

class ModelB
{
    public string Make { get; set; }
    public ObservableCollection<ModelC> Cs { get; set; }
}

class ModelC
{
    public string Brand{ get; set; }
}

I have a ModelAViewModel which permits access to the collection of ModelB Bs property. In this instance I have not created a ViewModel for ModelB. I have styled the ModelB and ModelC collections (and individual instances) by using DataTemplates:

<DataTemplate x:Key="modelATemplate">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
            <ItemsControl  ItemsSource="{Binding Bs}" ItemTemplate="{StaticResource modelBTemplate}"/>
        </ScrollViewer>
    </Grid>
</DataTemplate>

<DataTemplate x:Key="modelBTemplate">
    <Grid Margin="5" HorizontalAlignment="Center">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="{Binding Make}">
        <ItemsControl Grid.Row="1" ItemsSource="{Binding Mode=OneWay, Path=Cs}"
                  ItemTemplate="{StaticResource ResourceKey=modelCTemplate}">
        </ItemsControl>
    </Grid>
</DataTemplate>

I have been advised that this is not the MVVM way of doing things and that each entity, that is, ModelB and ModelC should have their own ViewModel. I have been told to keep the Model classes but create ViewModels for them. I am unable to visualise how this is going to work.

If I create a ModelBViewModel:

public class ModelBViewModel
{
     ModelB MyModelB { get; set; }
}

I have a predicament - I already have ModelB instances within the ModelA class, I would now have other ModelB instances in the ModelBViewModel. Is it necessary to iterate through the original ModelB collection within ModelA and create the ModelBViewModels, setting the MyModelB property to match that in ModelA as I go? It seems a bit complicated for what should be rather simple?

monster
  • 1,762
  • 3
  • 20
  • 38
  • Your models are using `ObservableCollection` (to rise notification), but you didn't implement notification of any kind for e.g. `ModelB.Make`, `ModelB.Cs` and `ModelC.Brand` changes. So while technically you display them it's not MVVM. Typically models have to provide a mechanism to inform ViewModel (`INotifyPropertyChanged` is good), while ViewModels nearly `must` implement `INotifyPropertyChanged`. Probably the easier is to make `ModelB`/`ModelC` implementing `INotifyPropertyChanged`, then they are actually become ViewModels which can be used directly for bindings. – Sinatr Jan 04 '17 at 10:28
  • See [this](http://stackoverflow.com/q/7767218/1997232) for why binding to something not implementing `INotifyPropertyChanged` is working. TLDR; changes made to models will only be visible in the view if view itself made them. – Sinatr Jan 04 '17 at 10:30
  • @Sinatr Not raising a notification does not mean that "it's not MVVM". – dvjanm Jan 31 '17 at 20:51

1 Answers1

1

MVVM means "Model View ViewModel". As you can see, the name contains Model and ViewModel. The idea is to have a dedicated ViewModel class for every Model class you have.

The ViewModel should contain view-specific properties and logic while the Model class should contain business model specfic properties and logic.

And: Yes, for some very simple MVVM examples this might be overhead. However you benefit from this seperation as soon as your view logic starts to diverge from your business logic.

If it is necessary to iterate through the original properties: I would say: Yes! I usually do it this way:

public class ModelBViewModel
{
    private ModelB _model;

    public ObservableCollection<ModelCViewModel> CVms { get; set; }

    public ModelBViewModel(ModelB model) {
        _model = model;
        CVms = new ObservableCollection();
        foreach(var modelC in model.Cs) {
            CVms.Add(new ModelCViewModel(modelC));
        }
    }
}
makzr
  • 355
  • 2
  • 7
  • 1
    Use Linq: `CVms = new ObservableCollection(model.Cs.Select(c=> new ModelCViewModel(c)));` – Nawed Nabi Zada Jan 04 '17 at 10:19
  • 3
    I always thought that one should create a ViewModel for each View not for each Model. Otherwise what good does it provide besides MVC – zahir Jan 05 '17 at 21:27
  • 1
    Yes, you should create a view model for each view. However, when you want to display your model data in more than one view it can make it easier to reuse your view model code when you have a view model for each model. – makzr Jan 06 '17 at 08:32
  • 1
    @makzr do you possibly have a larger example that puts what you’ve written here in context? I have pretty much the *exact* same question as OP, and I also feel that manually iterating through the collection to create a VM for each model instance seems clunky next to, say, setting a data template and then just using a contentcontrol, which is so much more, like, “automatic”. – Jonathan Tuzman Jun 02 '20 at 01:04