tags:

views:

1410

answers:

5

In the neighbour post: http://stackoverflow.com/questions/501886/wpf-mvvm-newbie-how-should-the-viewmodel-close-the-form/2100824#2100824 I've posted my vision how to close windows with MVVM usage. And now I have a question: how to open them.

I have a main window (main view). If user clicks on the "Show" button then "Demo" window (modal dialog) should be displayed. What is a preferable way to create and open windows using MVVM pattern? I see two general approaches:

The 1st one (probably the simplest). Event handler "ShowButton_Click" should be implemented in the code behind of the main window in way like this:

        private void ModifyButton_Click(object sender, RoutedEventArgs e)
        {
            ShowWindow wnd = new ShowWindow(anyKindOfData);
            bool? res = wnd.ShowDialog();
            if (res != null && res.Value)
            {
                //  ... store changes if neecssary
            }
        }
  1. If we "Show" button state should be changed (enabled/disabled) we will need to add logic that will manage button state;
  2. The source code is very similar to "old-style" WinForms and MFC sources - I not sure if this is good or bad, please advise.
  3. Something else that I've missed?

Another approach:

In the MainWindowViewModel we will implement "ShowCommand" property that will return ICommand interface of the command. Comman in turn:

  • will raise "ShowDialogEvent";
  • will manage button state.

This approach will be more suitable for the MVVM but will require additional coding: ViewModel class can't "show dialog" so MainWindowViewModel will only raise "ShowDialogEvent", the MainWindowView we will need to add event handler in its MainWindow_Loaded method, something like this:

((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;

(ShowDialog - similar to the 'ModifyButton_Click' method.)

So my questions are: 1. Do you see any other approach? 2. Do you think one of the listed is good or bad? (why?)

Any other thoughts are welcome.

Thanks.

+1  A: 

I use a controller which handles all information passing between views. All viewmodels use methods in the controller to request more information which can be implemented as dialogs, other views etc.

It looks something like this:

class MainViewModel {
    public MainViewModel(IView view, IModel model, IController controller) {
       mModel = model;
       mController = controller;
       mView = view;
       view.DataContext = this;
    }

    public ICommand ShowCommand = new DelegateCommand(o=> {
                  mResult = controller.GetSomeData(mSomeData);
                                                      });
}

class Controller : IController {
    public void OpenMainView() {
        IView view = new MainView();
        new MainViewModel(view, somemodel, this);
    }

    public int GetSomeData(object anyKindOfData) {
      ShowWindow wnd = new ShowWindow(anyKindOfData);
      bool? res = wnd.ShowDialog();
      ...
    }
}
adrianm
Thanks for your answer, but could you please clarify, what is "view" variable in the MainViewModel constructor? Do you have a reference to the View from your ModelView (that brokes MVVM approach)? Another question: how do you opening the window with Controller.OpenMainView? I see you are creating an instance of the "MainViewModel", but I don't see where view is created...? Thanks.
Budda
Fixed the missing references. The view,model and viewmodel has to be created and connected somewhere. As an example I did it in the controller but it could just as well be handled in an IoC container.Why do you think referencing a view from the viewmodel breaks MVVM?.
adrianm
Thanks for your answer. Please see the article:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx"Unlike the Presenter in MVP, a ViewModel does not need a reference to a view" That's why I would like to avoid referencing to the View from the ModelView.
Budda
Notice that I don't reference the real view, just an IView interface which most of time just contain DataContext and Close(). I find it unnecessarily complicated to use an IoC container if I don't assign the datacontext in the viewmodel and databinding Close doesn't make sense to me.
adrianm
I just override the App.OnStartup() method, create my main view there and pass it the view model. I watched a video on MVVM by Jason Dolinger, and that's how he did it. It doesn't seem good to pass even an interface reference (which really is still a reference, right?) to the view model.
Benny Jobigan
A: 

Take a look at my current MVVM solution for showing Modal Dialogs in Silverlight. It solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.

See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4

Roboblob
A: 

My approach is similar to adrianm’s. However, in my case the Controller never works with the concrete View types. The Controller is completely decoupled of the View - in the same way as the ViewModel.

How this works can be seen in the ViewModel example of WPF Application Framework (WAF).

.

Best Regards,

jbe

jbe
+2  A: 

I was thinking about this issue recently too. Here's an idea I had if you use Unity in your project as a 'container' or whatever for dependency injection. I guess normally you'd override App.OnStartup() and create your model, view model, and view there, and give each the appropriate references. Using Unity, you give the container a reference to the model, then use the container to 'resolve' the view. The Unity container injects your view model, so you never directly instantiate it. Once your view is resolved, you call Show() on it.

In an example video I watched, the Unity container was created as a local variable in OnStartup. What if you created it as a public static readonly property in your App class? You could then use it in your main view model to create your new windows, automatically injecting whatever resources the new view needs. Something like App.Container.Resolve<MyChildView>().ShowDialog();.

I suppose you could somehow mock the result of that call to the Unity container in your tests. Alternatively, perhaps you could write methods like ShowMyChildView() in the App class, which basically just does what I described above. It might be easy to mock a call to App.ShowMyChildView() since it would just return a bool?, eh?

Well, that might not really be any better than just using new MyChildView(), but it's a little idea I had. I thought I'd share it. =)

Benny Jobigan
After I got some experience, Unity usage is best what I saw.
Budda
A: 

Some MVVM frameworks (e.g. MVVM Light) make use of the Mediator pattern. So to open a new Window (or create any View) some View-specific code will subscribe to messages from the mediator and the ViewModel will send those messages.

Like this:

Subsription

Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
     // Instantiate new view depending on the message details
}

In ViewModel

Messenger.Default.Send(new DialogMessage(...));

I prefer to do the subscription in a singleton class, which "lives" as long as the UI part of the application does. To sum up: ViewModel passes messages like "I need to create a view" and the UI listens to those messages and acts on them.

There's no "ideal" approach though, for sure.

arconaut
Hi arconaut, Would you mind clearing, where do you actually put the Subscription code? In the code behind of the view?
nabeelfarid
It can be in a code-behind of some "static" view, e.g. main window. Or better in a separate class responsible for instantiating views.
arconaut