tags:

views:

2024

answers:

4

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?

A: 

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
+11  A: 

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.

Kiff
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
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
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
@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.
Kiff
+15  A: 
Micah
Thanks for clarification.
Jab
No problem. An upvote would be nice :)
Micah
+2  A: 

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