18

I've just started looking into M-V-VM for a WPF application. Everything makes sense so far besides this particular issue...

I have a ViewModel I'll call Search. This ViewModel binds to a datagrid and lists results of items. Now, I have a command that needs to bring up another view, the item's details.

Putting the logic to show another view in the Search View doesn't seem right, it's not testable at all.

Here is my ViewModel implementation, which is not testable...

public class SearchViewModel
{
   public void SelectItem()
   {
     // I want to call the DetailsView from here
     // this seems wrong, and is untestable
     var detailsView = new DetailsView();
     detailsView.Show();
   }
}

Where does the logic to show a view from a ViewModel method go in this pattern?

Jab
  • 13,713
  • 2
  • 42
  • 48

5 Answers5

19

As Kiff noted:

Views should never be instantiated anywhere "below" the UI layer. VMs exist below that realm, therefore this is not the place to put that logic (as you've already realized).

There will almost always be some UI level event that will indicate the need to create the view. In your example, it might be a row (double) click event on the datagrid. That would be the place to new-up and show your DetailsView window.

You have to realize that M-V-VM is slightly different than other patterns like MVC or MVP. The ViewModel has no direct knowledge of the UI. Opening another view is a view-specific function. The View Model should care less what or how many views are using it's data. I most likely would never open the view via a command.

alt text

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Micah
  • 111,873
  • 86
  • 233
  • 325
13

Views should never be instantiated anywhere "below" the UI layer. VMs exist below that realm, therefore this is not the place to put that logic (as you've already realized).

There will almost always be some UI level event that will indicate the need to create the view. In your example, it might be a row (double) click event on the datagrid. That would be the place to new-up and show your DetailsView window.

Chris Staley
  • 2,370
  • 1
  • 20
  • 21
  • Thanks for the help, this seems like a viable solution. Even though I can't unit test the logic for setting up the view(Setting properties that interact with the injected ViewModel), it leaves the ViewModels testable which is where the majority of logic is. – Jab Nov 21 '08 at 16:47
  • 1
    If a simple event opens the view, this is good. But what if the event needs some more action, data fetching, verification. Would you put this stuff into the view, too? Or create another level of indirection? – Sam Jan 08 '09 at 09:51
  • Indeed. Same question as Sam has over here. What if you need more data or logic before opening the view? (ex. if property x=1 open view 1, if property x=2 open other view) – Tom Deleu Mar 27 '09 at 11:31
  • @Sam @Tom: the event's args would pass down any data needed from the UI. The handler then uses that data to make its decisions, and/or retrieve further data. I like to put my event handlers on a controller, to keep my VMs as light as possible. Take a look at the event aggregation and commanding patterns in Prism 2.0 for a good example. It actually shows a few different approaches. – Chris Staley May 06 '09 at 17:03
4

Here's a basic rule of thumb on this.

  • If you are handling local actions in your view, you can intiate from the view model.

  • If it's cross view (like showing a search screen), then either use an EventAggregator pattern (an eventing service) or inject an Application Controller which you invoke methods on, and it in turn displays the search.

Glenn Block
  • 8,463
  • 1
  • 32
  • 34
1

Catel includes an approach which involves the use of IUIVisualizerService. This interface defines a UI controller which can be used to display dialogs in either modal or modaless form from a ViewModel. Basically, inside the parent vm, you create the viewmodel that should stay behind the new view, and the service finds the associated one (based on some convention or registration) and then shows it.

g1ga
  • 323
  • 3
  • 11
0

We use a variant on this pattern, Here we have controllers that represent the VM, so the datacontext of the View is the VM and our DTOs are properties of the VM/Controller. We call it a controller still as we use this as the control point and thus handle certain command from the View. This is (I think) where we would implement the command such as yours.

Preet Sangha
  • 64,563
  • 18
  • 145
  • 216