-1

In MVVM I was reading that the View and the Model should not know about each other. (C# example)

I have the ViewModel set up such that the ViewModel takes an instance of the Model as an input argument. This is done to support Dependency Injection via the constructor to help with Unit Testing, but I am not using a Dependency Injection Framework.

ViewModel(IModel ModelInstance){}

If the View is responsible for creating the View Model, is the following a violation of MVVM?

View()
{
   Model myModel = new Model ();
   this.DataContext = new ViewModel(myModel);
}

This seems like an MVVM violation because the View knows about the Model, as it is explictly creating the Model.

Is this an MVVM violation? What have folks done to work around this problem?

I have read the other similar MVVM questions and I still wasn't seeing any concrete ways to address this. How should a Model get passed into the ViewModel?

Specifically I'm asking if the View creates the ViewModel, who is responsible for creating the model that gets passed into the ViewModel

sdub0800
  • 139
  • 1
  • 9
  • 1
    These kinds of questions gain a lot of clarity if you ask a new one: "What is my purpose here?" What do I gain or achieve by using the technique at hand? What are the benefits? What are my costs? Do the benefits exceed the costs? – Robert Harvey Jan 13 '23 at 10:34

3 Answers3

1

I use dependency injection, so the VM gets injected into the view, and the "application logic" classes (aka models) get injected into the VM.

DI is the way to go if possible, but if you can't/don't want to use DI then the simplest option would be for the view to instantiate the VM, and the VM to instantiate the model. This isn't very unit testing friendly though, as you won't be able to "mock" the model.

Another option is to use "factories", so the view would call a (usually) static factory class method that is responsible for instantiating the model, instantiating the VM (passing the model to its ctr), then returning the VM. This approach would support unit testing of the VM, as you would be able to pass a mock model to its ctr.

Edit 1 - in response to first comment When it comes to passing the VM into the view, something, somewhere needs to be responsible for instantiating the view. That mechanism in turn needs to instantiate the VM (that the view is expecting), then instantiate the model that the VM is expecting, and so on up the chain of dependencies. This can get very messy without a DI framework.

Many MVVM frameworks include some kind of "navigation service" that sits on top of the DI framework and abstracts away (and vastly simplifies) much of what we're talking about here. Let's say you click a button to navigate to a different view - the button's ICommand code will request navigation via the navigation service, passing it a view name or perhaps the view's type (depending on the MVVM framework). The framework (in conjunction with the DI framework) will then instantiate the correct view. This will in turn result in the DI framework instantiating any dependencies that the view has (e.g. the VM), resolve it's dependencies (the model), and so on.

It's hard to provide an example without knowing the architecture of your app, how you navigate between views, and so on. Also, I've always used DI (and 'Prism' for navigation), so I'd probably have a hard time rolling a bespoke solution!

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152
  • Thanks Andrew. Can you clarify (in code preferably) how the VM would be injected into the view? I'd disagree that the model wouldn't be able to be mocked. You can still achieve DI with out a DI frame work. That dependecy injection happens through the viewmodel constructor, taking in an interface for the model (IModel). I think you could mock the model in unit tests for the view model by passing it any class that implements IModel. – sdub0800 Jan 13 '23 at 14:25
  • @sdub0800 see my edit. I don't know what your level of WPF experience is so apologies if I'm stating the obvious in any of that! – Andrew Stephens Jan 13 '23 at 15:37
0

MVVM isn't like a religion where the inquisition drags an unbeliever away to torture and burn at the stake.

If you worked in a team then your code wouldn't get past the first merge request review. But whoever reviewed it would presumably have told you "this is bad" when they rejected it. And told you what accepted practice is.

That hasn't happened so I guess there's just you working on this and you can do what you like.

Having said that.

That code does not follow the MVVM pattern.

The view shouldn't go get some data to pass into a viewmodel. It's the viewmodel's job to adapt and present it with data.

The model isn't a class like in MVC usage, it's whatever provides data. Often a wrapper round a Rest call like httpget.

I recommend viewmodel first wherever practical and a single window application.

The view is then a datatemplate that data from the viewmodel is templated out into.

Like this how to open new WPF window in stack panel in WPF mainwindow?

At it's simplest, mainwindowviewmodel controls what viewmodel is current and that in turn is templated.

You'd want more sophistication in practice.

In this though, the view does not go get it's viewmodel.

Let's differentiate between parent viewmodels and children. Frequently you will have a viewmodel for viewing or editing a bunch of things. See all invoices for a client or some such.

That parent viewmodel needs a bunch of data.

A pattern I have frequently used is to separate out instantiation of that parent viewmodel from fetching data.

The ctor is pretty much empty and there's an async task which will get any required data. You can instantiate and inject services, present your viewmodel so the view starts to render. Then separately and asynchronously get your data. The view appears with it's loading spinner, the task completes and the spinner is collapsed the data shown.

That task can be included in an interface all viewmodels implement so you can then generically instantiate a vm (usually out a di container) and await a GetMyData Task.

In a commercial apps the model is a call to a separate web site - web api or node or whatever.

I recommend separation of classes that model returns from viewmodels. Even if you're "just" using dapper to fill a class. Even if you have entity framework and those entity classes look like maybe you could "just" use them. Think of how you get that data as a separate model with separate dto classes.

Copying property A from one class to another makes for some repetitive tedious code. There are packages will make that easy. I like automapper. You then have a fracture point where you can conveniently insert any translation. Have a string in your model but want a datetime in your viewmodel? No problem you just put a bit of a lambda in the automapper definition.

When you want to commit data, you then do the reverse viewmodel => automapper => model.

When you want to edit a viewmodel automapper has another bonus in that you can deep copy any instance easily. Edit a copy. If it fails valiation then you can bin that copy and your original is pristine.

In this way though.

Any Viewmodel is presented to the UI and templated into controls.

A parent viewmodel gets it's data by calling a service.

That service "just" returns or accepts dto from the VMs perspective. The service hides whatever is beyond it.

Perhaps some prototyp-ish code. Let's assume we have a small app and no web server. There's just a repository returns data.

We have SomeViewModel and SomeViewmodel needs a collection of Somes.

Where does it get them?

Out a service which supplies a List where that Some is a DTO.

Where's it get that service?

That's resolved out dependency injection so it's passed into the ctor. The view knows nothing about any viewmodels, repositories or anything. SomeDataRepository can have an interface so you resolve against ISomeDataRepository and you can switch out moqs if you want to for unit testing. Or some other implementation of the repository if your requirement is very very unusual.

    public SomeViewModel(SomeDataRepository repos) 
    { 

In SomeViewModel there's a list filled using some mapper conversion

    SomeList = MapperConversion<SomeViewModel>(await repos.GetSomeAsync);

The repository Some dto is somewhat loosely coupled with the SomeViewModel since mapper copies property to property.

Andy
  • 11,864
  • 2
  • 17
  • 20
  • Thanks for the response Andy. Is there any code you could provide as an example? I'm still not seeing what class is responsible for creating the model. – sdub0800 Jan 13 '23 at 14:22
  • Thanks for the update. "The view knows nothing about any viewmodels, repositories or anything." Doesn't the View need to know about the ViewModel in order to have the right databindings to the viewmodel? (in wpf this is the DataContext) Seems like what you're saying is that the DI or IoC container would resolve the building of the Model. In your example SomeDataRepository would be handled by the IoC container in a DI framework. – sdub0800 Jan 13 '23 at 15:52
  • The view need to bind of course. But it is just looking for a Foo property on whatever is it's datacontext. It doesn't rely on Foo being from a specific class. – Andy Jan 13 '23 at 16:21
  • On this simple sort of an app, the repository is obtained out the di container or just passed in when the view's parent viewmodel is instantiated. The repository is the model in mvvm in this. Again, the model is not the dto that the repository returns when you call some get data on it. The model is whatever layer the viewmodel interacts with for data. – Andy Jan 13 '23 at 16:38
0

The constructors are not the problem. They are fine. The View Model, that is usually composed of View Model and Model classes. It therefore has explicit dependencies. The View Model class can either instantiate this dependencies on its own or, to improve extensibility, request them via a constructor (or property).

If you decide to use constructor injection, you must also use a DI container that resolves the dependencies - automatically. If done correctly you don't have to use the new keyword anymore.

You don't want the classes of the View to know any internals of View Model classes. This includes that they shouldn't know the Model classes that the View Model classes depend on.

The MVVM dependency graph

View ---> View Model ---> Model

If you you decide to compose objects using constructors, you usually chose an IoC framework to do it for you. This will also solve the instantiation problem (to prevent the View classes to know any Model classes).

The following example uses the .NET dependency injection library (Microsoft.Extensions.DependencyInjection namespace).

The key is to start the application manually. This way you have control over the flow and you can configure the dependency contaioner before the application has started:

App.xaml
Register a Application.Startup event handler to manually start the application.

<Application Startup="App_OnStartup">

</Application>

App.xaml
Configure the IoC container of your choice and start the application.

using Microsoft.Extensions.DependencyInjection;

class App : Application
{
  private void App_OnStartup(object sender, StartupEventArgs e)
  {
    ServiceProvider container = new ServiceCollection()
      .AddSingleton<IModel, Model>()
      .AddSingleton<IViewModel, ViewModel>()
      .AddSingleton<MainWindow>()
      .BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true });

    MainWindow mainWindow = container.GetRequiredService<MainWindow>();
    //mainWindow.Closed += ShutdownApplication_OnMainWindowClosed;
    mainWindow.Show();
  }
}

ViewModel.cs

class ViewModel : IViewModel
{
  private IModel Model { get; }

  public ViewModel(IModel model) => this.Model = model;
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private IViewModel MainViewModel { get; }

  public MainWindow(IViewModel dataContext) 
  {
    this.DataContext = dataContext;
    this.MainViewModel = dataContext;
  }
}

Dependency injection in .NET
Tutorial: Use dependency injection in .NET

BionicCode
  • 1
  • 4
  • 28
  • 44