0

I have a Window, which contains a Button AddParameter. This Button has an Event called Button_Click.

Staying within the MVVM pattern, is it allowed to open a new window with a simple Button_Click? As far as I understood it, the code-behind of the View still counts as View:

private void Button_Click(object sender, RoutedEventArgs e) {
    AddParameterWindow addParamWindow = new AddParameterWindow();
    addParamWindow.Show();
}

Doing that with ICommands seems rather unnecessary, so I wanted to know if this would still count as a clean MVVM solution.

Tseng
  • 61,549
  • 15
  • 193
  • 205
Kohnarik
  • 377
  • 2
  • 5
  • 19
  • 1
    well, if you use "Button_Click" you are not in MVVM already. Why don't you just open window with a client side script ? – Jurion Feb 18 '16 at 07:53
  • 2
    You could argue about it. The reason for MVVM is to split logic and presentation. If you need to open a Window or display another view no matter what presentation mechanism you chose i would say it counts as logic and you violate the MVVM pattern. If its not common logic you could go with your version. But i like to get rid of every bit of code behind and experimented with seperate view controlers, eventbuses and such to solve problems like these. But all got its drawbacks – BoeseB Feb 18 '16 at 07:54
  • 2
    @Jurion client side script? This is wpf sir. – Gusdor Feb 18 '16 at 08:01
  • 1
    People can get pretty philosophical about MVVM, but I've found that there are no practical reasons not to put event handlers in the code-behind. I would avoid putting any logic that you would want to write unit tests for, but for something purely view related (as you have in your example), you're not violating the pattern as I'd define it. I would ask, though, whether a window is what you really need. If you can get away with just floating or swapping out a user control, you might find it results in a better user experience *and* is a better fit for the MVVM pattern. – devuxer Feb 18 '16 at 08:10
  • @BoeseB I see. So basically it is correct, but not necessarily a very good option. Thank you. – Kohnarik Feb 18 '16 at 08:16
  • @devuxer I thought about it and found an additional window to be the best solution, given that this is a minor problem and not really something I have to do perfectly. Your comment helped me understand it a little bit better. Thank you. – Kohnarik Feb 18 '16 at 08:19

2 Answers2

1

I don't think there is anything at all wrong with opening a window from another window in MVVM. The MVVM pattern is about separation of concerns in terms of ViewModels (and underlying models) being represented in any way necessary without it knowing anything about the View (see here for a good intro).

However, I think you have to ask yourself if making a new Window is really a good feature. Have you seen applications spawn another Window, and do you like that behavior? Have you given popups a thought which can look like Windows and can bind to the same ViewModel as the Window or UserControl it is logically under? Personally I avoid instantiating new Windows because I can centralize things that I want to appear in every View, like Styles, timeout Timers, etc.

Community
  • 1
  • 1
Tyress
  • 3,573
  • 2
  • 22
  • 45
  • From my point of view the ViewModel is supposed to control the program flow, therefore it should be responsible for opening new windows. If it doesn't it gets pretty tricky to provide the new Window with necessary information (e.g. what data to display). – Tim Pohlmann Feb 18 '16 at 10:14
  • 1
    @TimPohlmann you can make the ViewModel responsible for controlling the program flow and opening new views, but it doesn't need to know the views. I particularly like implementing something like [this](http://stackoverflow.com/a/19654812/1685167) so that I'm "opening" ViewModels instead...in that case I don't have to worry about what data to display, because the View is always bound to the right ViewModel – Tyress Feb 18 '16 at 10:32
  • Going by the linked example you change the ViewModel property and then open another Window from code behind? – Tim Pohlmann Feb 18 '16 at 10:38
  • If your ViewModel is bound on a ContentControl on the current Window, changing the ViewModel property should change the View within the Window immediately – Tyress Feb 18 '16 at 10:51
  • That's true, but sometimes I want to open a new window ;) – Tim Pohlmann Feb 18 '16 at 10:55
-1

You can ofcourse use the event Button_Click to open a new window, but that is now out of MVVM. This maybe not right or good practice with MVVM, but this is how I do it:

assuming you have a ViewModelBase.cs that is something like this:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

I have a DelegateCommand.cs that extends ICommand:

public class DelegateCommand : ICommand
{
    private readonly Action _action;

    public DelegateCommand(Action action)
    {
        _action = action;
    }

    public void Execute(object parameter)
    {
        _action();
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

#pragma warning disable 67
    public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}

Now in your SampleViewModel.cs:

public class SampleViewModel : ViewModelBase
{
    public SampleViewModel()
    {

    }

    public ICommand OpenWindowCommand
    {
        get { return new DelegateCommand(OpenSampleWindow); }
    }

    private void OpenSampleWindow()
    {
        var sampleWindow = new SampleWindow();
        sampleWindow.Show();
    }
}

Now in your View you can now bind your command to your button:

<Button Command="{Binding OpenWindowCommand}"/>
jomsk1e
  • 3,585
  • 7
  • 34
  • 59
  • good example. this is how i would do it aswell. one small remark on jomsk1e's example: the OpenWindowCommand should return a private DelegateCommand field, which is instantiated once (usually inside OpenWindowCommand.get{ } the first time it is referenced). As is now, a new command is created everytime the button is pressed. – Fredrik Feb 18 '16 at 08:38
  • 4
    This works, but doesn't that violate the MVVM principles? Within your `OpenSampleWindow()`, you create a new instance of the Window I want to open, but that's a direct access to the view, isn't it? – Kohnarik Feb 18 '16 at 08:42
  • @Kohnarik You are right this example is worse than your solution, because this way you will leak a dependecy to the Presentation Framework you use into the viewmodel, which violates MVVM. You want your ViewModels without any presentation framework code. – BoeseB Feb 18 '16 at 08:45
  • @BoeseB I thought so. Using a similar approach, but applying the MVVM principles would be a little bit more complicated. – Kohnarik Feb 18 '16 at 08:49
  • 2
    Until `OpenSampleWindow()` it's still mvvm, after - it's not. You have to delegate window creating process away from viewmodel ([click](http://stackoverflow.com/a/16653029/1997232)). – Sinatr Feb 18 '16 at 08:51
  • @Sinatr Thank you for the reference. – Kohnarik Feb 18 '16 at 08:57
  • 1
    @Sinatr the only problem I see with your linked answer is, where you inject that interface. If you create the ViewModel in another VM and instantiate the concrete ProductionWindowFactory class to pass to the newly created VM you just passed the problem to another VM. What i want to say you have to be careful where you create the VM with this aproach. A DependencyInjection Framework could solve this problem, but i wouldnt add one to my project just for this one use – BoeseB Feb 18 '16 at 08:57
  • we are all learning here, like what I said in my answer, I maybe right or wrong with my approach. Please show us, how to properly do this @BoeseB and Sinatr Thank you. – jomsk1e Feb 18 '16 at 09:19
  • There are different aproaches to solve this problem. The WindowFactory interface from Sinatr's link is one way but it only works if you take care where you create the actual instance of the interface implementation. It should be created in your integration logic either manually and being passed down the class hierarchy or via DI-Container. Another approach is the MVCVM Pattern which adds an [controller](http://stackoverflow.com/a/4339420/4369295) to the mix whichs job is to manage the creation of views and viewmodels. – BoeseB Feb 18 '16 at 10:08
  • 1
    The key of MVVM is to isolate your ViewModels from the UI-Framework. If you have to add references to the WPF or Winforms library if you put your ViewModels in their own Assembly, then there is something smelly in your implementation of MVVM – BoeseB Feb 18 '16 at 10:10