views:

1001

answers:

7

I have an MVVM application. In one of the ViewModels is the 'FindFilesCommand' which populates an ObservableCollection. I then implement a 'RemoveFilesCommand' in the same ViewModel. This command then brings up a window to get some more user input.

Where/what is the best way to do this whilst keeping with the MVVM paradigm? Somehow doing:

new WhateverWindow( ).Show( ) 

in the ViewModel seems wrong.

Cheers,

Steve

A: 

For Dialogs of this sort. I define it as a nested class of the FindFilesCommand. If the basic dialog used among many commands I define it in a module accessible to those commands and have the command configure the dialog accordingly.

The command objects are enough to show how the dialog is interacting with the rest of the software. In my own software the Command objects reside in their own libraries so dialog are hidden from the rest of the system.

To do anything fancier is overkill in my opinion. In addition trying to keep it at the highest level often involving creating a lot of extra interfaces and registration methods. It is a lot of coding for little gain.

Like with any framework slavish devotion will lead you down some strange alleyways. You need to use judgment to see if there are other techniques to use when you get a bad code smell. Again in my opinion dialogs should be tightly bound and defined next to the command that use them. That way five years later I can come back to that section of the code and see everything that command is dealing with.

Again in the few instances that a dialog is useful to multiple commands I define it in a module common to all of them. However in my software maybe 1 out of 20 dialogs is like this. The main exception being the file open/save dialog. If a dialog is used by dozens of commands then I would go the full route of defining a interface, creating a form to implement that interface and registering that form.

If Localization for international use is important to your application you will need to make sure you account for that with this scheme as all the forms are not in one module.

RS Conley
Hi. I think having concrete dialogs in the ViewModel would break testability.
Steve Dunn
A: 

I have run into this issue with MVVM as well. My first thought is to try to find a way to not use the dialog. Using WPF it is a lot easier to come up with a slicker way to do things than with a dialog.

When that is not possible, the best option seems to be to have the ViewModel call a Shared class to get the info from the user. The ViewModel should be completely unaware that a dialog is being shown.

So, as a simple example, if you needed the user to confirm a deletion, the ViewModel could call DialogHelper.ConfirmDeletion(), which would return a boolean of whether the user said yes or no. The actual showing of the dialog would be done in the Helper class.

For more advanced dialogs, returning lots of data, the helper method should return an object with all the info from the dialog in it.

I agree it is not the smoothest fit with the rest of MVVM, but I haven't found any better examples yet.

timothymcgrath
calling "DialogHelper.ConfirmDeletion()" of actually calling the dialog does not make any defference imho. You're still mixing pure UI in the code. (after all, DialogHelper already suggests it's a windowing helper...)
Tom Deleu
I agree, but I don't see any way better to do this. The dialog has to be displayed from the ViewModel to work. I think having the ViewModel at least not explicitly displaying the dialog is a good step, though. This way dialogs can be reused and compeltely replaced with minimal effort.
timothymcgrath
I'm thinking that maybe events could be the way to go? Not sure of the details yet though and how it'd fit in with the pure MVVM.
Steve Dunn
A: 

I'd have to say, Services are the way to go here.

The service interface provides a way of returning the data. Then the actual implementation of that service can show a dialog or whatever to get the information needed in the interface.

That way to test this you can mock the service interface in your tests, and the ViewModel is none the wiser. As far as the ViewModel is concerned, it asked a service for some information and it received what it needed.

Cameron MacFarland
Hi. Who'd own the service though? The View or the Model? Doesn't seem quite right in either of those places.
Steve Dunn
What do you mean who owns the service? It's a singleton. Also if you think about what the service is providing then it's the VM that calls it because that's what needs to confirm the delete, etc.
Cameron MacFarland
Sorry, I meant 'in what namespace does the service interface live'.
Steve Dunn
Ah. Not sure it matters, but you can put it in it's own namespace with other services.
Cameron MacFarland
+1  A: 

In the Southridge realty example of Jaime Rodriguez and Karl Shifflet, they are creating the window in the viewmodel, more specifically in the execute part of a bound command:

    protected void OnShowDetails ( object param ) 
    {
        // DetailsWindow window = new DetailsWindow();
        ListingDetailsWindow window = new ListingDetailsWindow();
        window.DataContext = new ListingDetailsViewModel ( param as Listing, this.CurrentProfile ) ; 
        ViewManager.Current.ShowWindow(window, true); 
    }

Here is the link: http://blogs.msdn.com/jaimer/archive/2009/02/10/m-v-vm-training-day-sample-application-and-decks.aspx

I guess thats not of a big problem. After all, the Viewmodel acts as the 'glue' between the view and the business layer/data layer, so imho it's normal to be coupled to the View (UI)...

Tom Deleu
Thanks for the response. Wouldn't this now mean that the ViewModel has a reference to the View for the ListingDetailsWindow (and also the ViewManager?).
Steve Dunn
+1  A: 

Onyx (http://www.codeplex.com/wpfonyx) will provide a fairly nice solution for this. As an example, look at the ICommonDialogProvider service, which can be used from a ViewModel like this:

ICommonFileDialogProvider provider = this.View.GetService<ICommonDialogProvider>();
IOpenFileDialog openDialog = provider.CreateOpenFileDialog();
// configure the IOpenFileDialog here... removed for brevity
openDialog.ShowDialog();

This is very similar to using the concrete OpenFileDialog, but is fully testable. The amount of decoupling you really need would be an implementation detail for you. For instance, in your case you may want a service that entirely hides the fact that you are using a dialog. Something along the lines of:

public interface IRemoveFiles
{
   string[] GetFilesToRemove();
}

IRemoveFiles removeFiles = this.View.GetService<IRemoveFiles>();
string[] files = removeFiles.GetFilesToRemove();

You then have to ensure the View has an implementation for the IRemoveFiles service, for which there's several options available to you.

Onyx isn't ready for release yet, but the code is fully working and usable at the very least as a reference point. I hope to release stabilize the V1 interface very shortly, and will release as soon as we have decent documentation and samples.

wekempf
Hi. The 'this.View.GetService( )': What does this.View return and would it need a specific reference to any of types in my 'view' namespace? To me, that would initally appear to break MVVM - mind you, I could be wrong - I'm very new to it
Steve Dunn
It returns a View instance, part of the Onyx framework. View is an IServiceProvider and also provides access to the ViewElement (the element in which the View is associated), as a DependencyObject. If you never use the ViewElement you're not violating MVVM principles. Check out the Simple sample.
wekempf
+1  A: 

I personally look at this scenario as one where the main window view model wants to surface a task for the end user to complete.

It should be responsible for creating the task, and initializing it. The view should be responsible for creating and showing the child window, and using the task as the newly instantiated window's view model.

The task can be canceled or committed. It raises a notification when it is completed.

The window uses the notification to close itself. The parent view model uses the notification to do additional work once the task has committed if there is followup work.

I believe this is as close to the natural/intuitive thing people do with their code-behind approach, but refactored to split the UI-independent concerns into a view model, without introducing additional conceptual overhead such as services etc.

I have an implementation of this for Silverlight. See http://www.nikhilk.net/ViewModel-Dialogs-Task-Pattern.aspx for more details... I'd love to hear comments/further suggestions on this.

NikhilK
A: 

What we are doing is somethng like that, what is described here: http://www.codeproject.com/KB/WPF/DialogBehavior.aspx?msg=3439968#xx3439968xx

The ViewModel has a property that is called ConfirmDeletionViewModel. As soon as I set the Property the Behavior opens the dialog (modal or not) and uses the ConfirmDeletionViewModel. In addition I am passing a delegate that is executed when the user wants to close the dialog. This is basically a delegate that sets the ConfirmDeletionViewModel property to null.

Florian