Home > Blockchain >  MVVM: View Creates ViewModel ViewModel takes Model as a Constructor argument. Is this an MVVM violat
MVVM: View Creates ViewModel ViewModel takes Model as a Constructor argument. Is this an MVVM violat

Time:01-14

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

CodePudding user response:

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!

CodePudding user response:

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.

  • Related