MVVM: Is code-behind evil or just pragmatic?

我怕爱的太早我们不能终老 提交于 2019-12-05 11:25:26

First, create an interface that contains only the Close method:

interface IClosable
{
    void Close();
}

Next, make your window implement IClosable:

class MyWindow : Window, IClosable
{
    public MyWindow()
    {
        InitializeComponent();
    }
}

Then let the view pass itself as IClosable as command parameter to the view model:

<Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

And lastly, the command calls Close:

CloseCommand = new DelegateCommand<IClosable>( view => view.Close() );

And what have we now?

  • we have a button that closes the window
  • we have no code in code-behind except , IClosable
  • the view model knows nothing about the view, it just gets an arbitrary object that can be closed
  • the command can easily be unit tested
gilmishal

There is nothing wrong or right with using code behind, this is mainly opinion based and depends on your preference.

This example shows how to close a window using an MVVM design pattern without code behind.

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/> 
<!-- the CommandParameter should bind to your window, either by name or relative or what way you choose, this will allow you to hold the window object and call window.Close() -->

basically you pass the window as a parameter to the command. IMO your viewmodel shouldn't be aware of the control, so this version is not that good. I would pass a Func<object>/ some interface to the viewmodel for closing the window using dependency injection.

Take a look at some toolkits e.g. MVVMLight has EventToCommand, which allows you to bind command to events. I generally try my best to limit logic in View, as it's harder to test it.

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"

...

<i:Interaction.Triggers>
   <i:EventTrigger EventName="Loaded">
     <command:EventToCommand Command="{Binding YourCommandInVM}"/>
   </i:EventTrigger>
</i:Interaction.Triggers>

Sometimes I use a work-around.

Assumes u have a view "MainWindow" and a viewmodel "MainWindowVM".

public class MainWindowVM
{
    private MainWindow mainWindow;
    public delegate void EventWithoudArg();
    public event EventWithoudArg Closed;

    public MainWindowVM()
    {
        mainWindow = new MainWindow();
        mainWindow.Closed += MainWindow_Closed;
        mainWindow.DataContext = this;
        mainWindow.Loaded += MainWindow_Loaded;
        mainWindow.Closing += MainWindow_Closing;
        mainWindow.Show();
    }

    private void MainWindow_Loaded()
    {
        //your code
    }

    private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        //your code
    }

    private void MainWindow_Closed()
    {
        Closed?.Invoke();
    }
}

Here I store my view in a private variable so you can access it if you need it. It breaks a bit the MVVM.
In my viewmodel, I create a new view and show it.
Here I also capture the closing event of the view an pass it to an own event.
You can also add a method to the .Loaded and .Closing events of your view.

In App.xaml.cs you just have to create a new viewmodel object.

public partial class App : Application
{
    public App()
    {
        MainWindowVM mainWindowVM = new MainWindowVM();
        mainWindowVM.Closed += Mwvm_Close;
    }

    private void Mwvm_Close()
    {
        this.Shutdown();
    }
}

I create a new viewmodel object and capture it own close-event and bind it to the shutdown method of the App.

Your description indicates that the view model is some kind of a document view. If that's correct then I would leave Save, Close, etc. to be handled by the document container e.g. the application or the main window, because these commands are on a level above the document in the same way as copy/paste are on the application level. In fact ApplicationCommands has predefined commands for both Save and Close which indicates a certain approach from the authors of the framework.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!