3

At the moment I have the following structure of my application:

  • A folder named ViewModels
  • A folder called Views
  • A folder called Services

The ViewModel has a class, called ItemTypeDetailViewModel. How it looks:

public class ItemTypeDetailViewModel
{
    private IItemTypenService itemTypeService;
    public ObservableCollection<Models.ItemType> ItemTypes { get; set; }
    public ICollectionView CollectionView { get; set; }

    public ItemTypeDetailViewModel()
    {
        itemTypeService = new ItemTypeService();
        itemTypeService.GetItemTypes();

        CollectionView = CollectionViewSource.GetDefaultView(ItemTypes);
    }
}

In the class I have a reference to the Service Layer (for now no DI, just newing in the constructor). With the help of the itemTypeService I will get a collection of ItemTypes. This is maybe a more MVP kind of structuring?

But now I start to get confused because most of the examples I see of MVVM have the model wrapped in a ViewModel.

What is a good approach here?

Garth Marenghi
  • 1,997
  • 5
  • 20
  • 38
  • 3
    The View should bind to a ViewModel. The ViewModel cannot know anything about potential views. You might write a new view to work with an existing ViewModel - or you might write unit tests for your ViewModel and so a view referred to in the ViewModel is inappropriate. – Kieren Johnstone Apr 01 '11 at 11:39
  • Further to that, I'd put your ViewModels in a separate project/assembly that does not reference anything UI or view-y, to enforce this. It works well :) – Kieren Johnstone Apr 01 '11 at 11:50
  • The View is not in anyway available in my ViewModel. Maybe you got confused with ICollectionView CollectionView? That is the view for a collection, not the GUI view. – Garth Marenghi Apr 01 '11 at 11:52
  • Sorry, correct, you have a class ending ViewModel that returns a class ending View. Is that a collection of ViewModels? Perhaps you could make it generic (`ICollectionView<*ViewModel>`). Finally you're exposing the underlying model `public`ly with `ItemTypes` - I personally think it's much better to keep the object graph entirely full of ViewModels only, or basic value types. – Kieren Johnstone Apr 01 '11 at 11:58
  • No, it is a view of the collectiontype, which makes me use things like filtering, grouping, paging etc. on the items in the collection. And as far as I see, it _is_ generic. I don't think I catch what you want to say in the last sentence though... – Garth Marenghi Apr 01 '11 at 12:03
  • I just realized I made a mistake in my topic header. View should be model... – Garth Marenghi Apr 01 '11 at 12:05

2 Answers2

2

(First part from my comment above: The View should bind to a ViewModel. The ViewModel cannot know anything about potential views. You might write a new view to work with an existing ViewModel - or you might write unit tests for your ViewModel and so a view referred to in the ViewModel is inappropriate.)

I'm not sure exactly what you're trying to do but this looks like the detail view for an ItemType. I can't see why then you'd have ALL item types queried within the constructor?

I would do something like this:

  • Create an ItemTypesViewModel, which has a collection of ItemTypeDetailViewModel, lazy-loaded
  • Create a repository somewhere that the ViewModels use to query the model (data) (e.g. the IItemTypeService does this I imagine)

A quick example, without the lazy-loading:

    public class ItemTypesViewModel : ViewModelBase
    {
        private List<ItemTypeDetailViewModel> itemTypeViewModels;

        public IEnumerable<ItemTypeDetailViewModel> ItemTypes
        {
            get
            {
                return itemTypeViewModels;
            }
        }

        public ItemTypesViewModel(IItemTypeService service)
        {
            // populate itemTypeViewModels using service here
        }
    }

Update, more relevant I think/hope: What I usually do in terms of accessing the service layer is have a static member in the ViewModel class that acts as a factory memory. Example:

public class ItemTypesViewModel : ViewModelBase
{
    public static ItemTypesViewModel Create(IItemTypeService service)
    {
        // build and return object here
    }
}

At some point you've got to cross the boundaries, and this method is to my personal taste.. would be interested to see what others do. Perhaps an additional layer or set of factories for ViewModels. ?

Kieren Johnstone
  • 41,277
  • 16
  • 94
  • 144
2

But now I start to get confused because most of the examples I see of MVVM have the model wrapped in a ViewModel.

What is a good approach here?

If each of your data items (a Model) is trivial, and the way you are using them in the View is also trivial, then there's no problem at all with exposing them "as is" from the ViewModel (creating extra work just because is never a good idea). On the other hand, if you find you need functionality that is not extremely easy to achieve it might be an indication that you need to wrap each Model in its own ViewModel (an ItemTypeViewModel).

An example of when you may not need a ViewModel is displaying the items in a read-only list, in which case you will be good to go with a DataTemplate and nothing more. The counter-example would be if you need to edit them and the edits need to be validated; the ViewModel will then hold the validation logic.

To sum it up: don't go berserk with MVVM if there's no reason to. Be practical.

A final note regarding the ViewModel having access to the View:

There is no problem in doing that as long as you do not use public interface of the view's runtime type. In fact, ViewModel-first MVVM requires that the ViewModel have a reference to the View. Many people will tell you that this is an automatic mistake -- they are wrong, and probably influenced by the fact that View-first (which is the MVVM flavor found in most examples) doesn't have the ViewModel reference the View.

If it is OK for the View to have a reference to the ViewModel as its DataContext, then it's also OK for the converse to happen. The important point is how that reference is used: public object View { get; set; } is certainly fine because it does not introduce any coupling; but public MyUserControl View { get; set; } is not, because it couples the ViewModel to the specific type MyUserControl.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • I may be being naive, but why would you give up decoupling View and ViewModels over keeping the Model and ViewModel decoupled? I.e. being able to unit-test the ViewModel vs.. well, what's the advantage? Being able to test the View+ViewModel together without the model being present? – Kieren Johnstone Apr 01 '11 at 12:17
  • @KierenJohnstone: Please explain what you mean "give up decoupling". Is the last paragraph unclear? As for the assertion that you don't necessarily need a ViewModel, the reasoning is that you don't wrap ViewModels around *everything* just because you can. Don't you agree with the "read-only POCO" example? – Jon Apr 01 '11 at 12:19
  • So, if I'm right, there can actually be *_two_* ViewModels? One is a ViewModel of the Model, and a ViewModel of the View. Either the ViewModel of the View has the actual Model, or the ViewModel of the View has the ViewModel of the Model – Garth Marenghi Apr 01 '11 at 12:28
  • @Jon I didn't see that last paragraph however I can't see it working [not suggesting that it's wrong - just that I can't see]; in almost all of my real-life apps there is at least one scenario where multiple views are referring to the same ViewModel (both views on-screen at the same time). Do you have a reference/source so I might see why it's useful or valid for the VM to refer to the V? – Kieren Johnstone Apr 01 '11 at 12:37
  • @Benjamin: No, the "ViewModel for the View" idea is certainly not good. I don't quite understand why you want to make a ViewModel for the View -- can you please clarify in the question? There can be many ViewModels, but all of them wrap Models, not Views. The ViewModels may also be "related" (i.e. a hierarchy) if the Models are also related. – Jon Apr 01 '11 at 12:37
  • @KierenJohnstone: http://stackoverflow.com/questions/3763072/what-are-the-pros-and-cons-of-view-first-vs-viewmodel-first-in-the-mvvm-pattern – Jon Apr 01 '11 at 12:43
  • Because that's how I thought I understood it. I always thought that the View had a corresponding ViewModel, just like the View in MVP has a Presenter. – Garth Marenghi Apr 01 '11 at 12:43
  • @KierenJohnstone: Please be aware I 'm not saying "you should do it this way", but rather "there's nothing wrong with doing it this way". Everyone should balance the pros and cons for themselves. – Jon Apr 01 '11 at 12:45
  • @Benjamin: The View *uses* a ViewModel (e.g. databinds to it), but the ViewModel does not *represent* the View; it represents the Model, but it is *used* by the View. – Jon Apr 01 '11 at 12:46
  • Aaaaah, I see. Lightbulb moment here. So you have a Model that you want to show in the View, and you can then wrap it with a ViewModel so that it presents itself nicely to the View. I think the reason why I thought differently was a small program by Josh Smith - in it he had a MainWindow and a MainWindowViewModel. – Garth Marenghi Apr 01 '11 at 12:59
  • @Benjamin: Exactly. That naming sounds confusing indeed. – Jon Apr 01 '11 at 13:03
  • Thank you for answering my plethora of questions :-) I guess that when you have a userControl that you use to compose a MainWindow, you can also use a ModelView for the UserControl? Because I also come across other MVVM applications that use it like this. – Garth Marenghi Apr 01 '11 at 13:28
  • Benjamin: Yes, because the `UserControl` is conceptually a View. Putting a View inside another View is called interface composition. – Jon Apr 01 '11 at 13:30
  • Right, so the UserControl has (or can have) a corresponding UserControlViewModel, and the UserControl itself can have a ViewModel of a Model. That explains a whole lot! – Garth Marenghi Apr 01 '11 at 13:39
  • @Benjamin: The `UserControl` will *use* a ViewModel. The ViewModel will not be created having the UserControl in mind at all -- only the Model. – Jon Apr 01 '11 at 13:44
  • @Jon In your last paragraph you mention that viewmodels can have untyped references to the view. What would these be used for? – Asad Saeeduddin Apr 11 '14 at 09:09
  • 1
    @Asad: They can be forwarded to other services. For example, a viewmodel might expose a "close" command that should result in its view(s) being removed from the UI. The vm cannot and should not know how to do that, but frameworks that provide infrastructure for view composition do. So you pass the view to the framework and ask it to remove it from the UI; in general the vm itself knows what logical steps need to be taken for a particular action, but not necessarily how to take all of them. – Jon Apr 11 '14 at 09:27
  • But shouldn't this close command be a method of the control, and not of the viewmodel? If any related logic involving your actual data needs to take place, such as disposing data contexts etc, this could be invoked against the viewmodel by the control when it is closed. Or is the control supposed to have no public interface, with the viewmodel exposing any and all functionality for the control? – Asad Saeeduddin Apr 11 '14 at 10:53
  • 1
    There might be a method of the control that "closes" it, or there might not be (e.g. a [`TabItem`](http://msdn.microsoft.com/en-us/library/system.windows.controls.tabitem.aspx) is removed by doing something to its parent, not to itself). But in any case the philosophy of MVVM is that you should not be directly interacting with the view. – Jon Apr 11 '14 at 11:08