12

I'm using MVVM in a WPF app. I'm very new to both. Let me state that I am not a purist in the MVVM pattern, I am trying to use as many best practices as I can but am trying to make what I think are reasonable compromises to make it work in our environment. For example, I am not trying to achieve 0% code in my View code-behind.

I have a couple of questions about best practices.

1) I understand I don't want my VM to know about the attached View, but is it reasonable for the View to have a reference to its VM?

2) If a control in a View opens another View (such as a dialog) should I handle this in the View? It seems wrong to handle it in the VM since then the VM has some knowledge of a specific View.

InteXX
  • 6,135
  • 6
  • 43
  • 80
BrettRobi
  • 3,793
  • 7
  • 40
  • 64

5 Answers5

14

1) The View has definitely a reference to the ViewModel through the DataContext. And you are allowed to cast the DataContext in your View:

public class ShellView : Window 
{
   …
   public ShellViewModel { get { return DataContext as ShellViewModel; } }

This isn’t a violation with the Model-View-ViewModel pattern.

.

2) You are right. A ViewModel shouldn’t open another View. A better approach is to use Controllers. They are responsible for the Workflow of an application.

If you are interested in more detailed information then you might have a look at the WPF Application Framework (WAF).

jbe
  • 6,976
  • 1
  • 43
  • 34
  • +1 To expand on jbe's explanation for #2 a bit, you can expose a command on the VM that is wired to a controller's command handler that will handle displaying the second view. In addition to WAF, you can also check out the P&P Team's Composite Application Library (aka Prism): http://compositewpf.codeplex.com/Wikipage – Chris Staley Mar 31 '10 at 20:22
  • Yeah, clearly the View knows about the VM through the DataContext, I guess I just hadn't seen that since all of the data binding in my xaml is so dependent on the VM that having a stronger reference to it in my Views codebehind hardly seems like a bad thing. And thanks for the tip on Controllers, I will look into that pattern. – BrettRobi Apr 01 '10 at 14:51
  • `public ShellViewModel Model { get { return DataContext as ShellViewModel; } }` – Paul-Sebastian Manole Nov 09 '13 at 13:34
  • 1
    There are two very different things: one is the View having a reference to ViewModel via DataContext. This is normal and desireable. What is NOT desireable (though sometimes it's needed) is the View _needing to know the ViewModel type_, as in the casting `DataContext as SomeVMType`. Ideally, only the types (and names) of public VM _properties_ should compose a "generic interface" between V and VM. – heltonbiker Oct 10 '14 at 13:11
  • @heltonbiker: I prefer type safety. The VS2013 XAML Editor comes with IntelliSense within the Binding path expressions. The Resharper Addin shows additional warnings if the Binding path contains errors. This only works if the View knows the type of the ViewModel. – jbe Nov 08 '14 at 13:57
  • Interesting. I am currently using VS 2010 + Expression Blend 4. It's nice to know that XAML has not been left behind regarding IDE tooling. – heltonbiker Nov 10 '14 at 12:41
3

1) Here are two simple practices for View's "knowing about" a ViewModel. It's reasonable for a View to know about a ViewModel (for Data Binding) -- but you may not need it in your case. See if either of these approaches help solve your problem. There are other ways, but these should be simple enough:

public View(ViewModel vm)
{
     View.DataContext = vm;
}

public Bootstrapper(View v, ViewModel vm)
{
     v.DataContext = vm;
     //or, if you want it to have no parameters
     View v = new View();
     ViewModel vm = new ViewModel();
     v.DataContext = vm;
}

The first option isn't bad if you have a service location tool, but there is a flavor of MVVM that doesn't like any code in the View's Code-Behind. The second option isn't bad either, should be simple enough for your task.

2.) This question can be a bit of a sticky point in MVVM design. If we are talking about a general Win32 MessageBox, I will often separate that logic into an additional object and put it in the VM. This way tends to a little more clear. (For example, I have selected an item in a ListBox, I have attached a Delete ICommand to that action, and in my ViewModel when this ICommand is Executed, I will poke my MessageBoxObject to ask if the user "wants to really delete" this item). More advanced "Dialogs" would use additional ViewModels and DataTemplates for those ViewModels. I prefer the Mediator approach.

Jimmy Lyke
  • 330
  • 1
  • 9
2

1). The view will need a reference to the view model at some level, since the viewmodel will act as the view's datacontext.

2) One way to handle this is to have a generalized viewmodel representing a dialog, that is owned by the main viewmodel (the one being used as the views datacontext.)

You can use a command to crate a new instance of a dialog viewmodel, which will have a corresponding datatemplate defined in your resources. This template will be set to bind to the dialogviewmodel type.

Pierreten
  • 9,917
  • 6
  • 37
  • 45
  • +1 but I think that for point 1), the OP was asking whether it is OK for the View class to know about the exact ViewModel class, rather than just bind on a datacontext of unknown type via property names. – Wim Coenen Mar 29 '10 at 17:23
  • Per Wim, indeed I was referring to the View class having a reference to the ViewModel, not just through databinding (which is abstract). – BrettRobi Mar 29 '10 at 17:28
  • On point #2. I'm using the MVVM Light toolkit in which I create Views, which then bind to a ViewModel through a Locator service. So I am not creating VM's, as they don't know how to attach a View to themselves. So it seems I need a way to create the dialog View, and where should that happen (View or VM)? – BrettRobi Mar 29 '10 at 17:30
2

Quite late, but I think this is tricky enough to deserve lots of different perspectives.


I understand I don't want my VM to know about the attached View, but is it reasonable for the View to have a reference to its VM?

As already answered, a proper View-ViewModel arrangement involves the ViewModel being assigned as the View's DataContext property. That allows DataBindings to be "automagically" established from declarative XAML, or fine-tuned via code behind.

Sometimes, you'll be tempted to write, in your code behind, something like this:

var dc = DataContext as CleverViewModel;
CleverViewModel.CleverProperty.Add(someValue); // just a simple example

I believe the proper way to achieve this sort of things is NOT to cast DataContext, but instead:

  1. Have some dedicated control in View, for example an ItemsControl with its ItemsSource two-way databound to some property in viewmodel:

    <ItemsSource x:Name="cleverControl" Visibility="Collapsed" ItemsSource="{Binding CleverProperty, Mode=TwoWay}"/>

  2. Cast the bound property instead of the whole ViewModel, in code behind:

    var collection = (ObservableCollection<double>)cleverControl.ItemsSource; collection.Add(someValue);

Note the important difference: the second approach in this example doesn't require the View to know the ViewModel type, it only needs a property named CleverProperty of type ObservableCollection<double>. This allows me to have polymorphic or even duck-typed ViewModels.


If a control in a View opens another View (such as a dialog) should I handle this in the View? It seems wrong to handle it in the VM since then the VM has some knowledge of a specific View.

This shouldn't happen in strict MVVM, and its not difficult to avoid using DataTemplates. DataTemplates map a given type of DataContext to a given type of view, so anytime the datacontext of a ContentControl changes, its display also changes, provided that you have a DataTemplate for that type:

  1. A control in the view could send a command to the ViewModel, which in turn would update some of its own properties, that would be reflected by the view.

  2. A View could contain another View, outside the knowledge of the ViewModel. In this case, the code behind can manipulate the datacontext of the contained view.

There are more subtleties, but I have been using this approach with good results. Hope this helps someone.

heltonbiker
  • 26,657
  • 28
  • 137
  • 252
1

Build Your Own MVVM Framework

I found the approach suggested by Rob Eisenberg very interesting.

Key points:

  1. Convention over configuration
  2. ViewModel first

Which is very similar to ASP.NET MVC philosophy.

I highly recommend watching the video.

andrey.tsykunov
  • 2,896
  • 2
  • 32
  • 21