Implementing “close window” command with MVVM

2019-01-17 19:44发布

So my first attempt did everything out of the code behind, and now I'm trying to refactor my code to use the MVVM pattern, following the guidance of the MVVM in the box information.

I've created a viewmodel class to match my view class, and I'm moving the code out of the code behind into the viewmodel starting with the commands.

My first snag is trying to implement a 'Close' button that closes the window if the data has not been modified. I've rigged up a CloseCommand to replace the 'onClick' method and all is good except for where the code tries to run this.Close(). Obviously, since the code has been moved from a window to a normal class, 'this' isn't a window and therefore isn't closeable. However, according to MVVM, the viewmodel doesn't know about the view, so i can't call view.Close().

Can someone suggest how I can close the window from the viewmodel command?

13条回答
手持菜刀,她持情操
2楼-- · 2019-01-17 19:48

Given a way, Please check

https://stackoverflow.com/a/30546407/3659387

Short Description

  1. Derive your ViewModel from INotifyPropertyChanged
  2. Create a observable property CloseDialog in ViewModel, Change CloseDialog property whenever you want to close the dialog.
  3. Attach a Handler in View for this property change
  4. Now you are almost done. In the event handler make DialogResult = true
查看更多
beautiful°
3楼-- · 2019-01-17 19:51

first of all give your window a name like

x:Name="AboutViewWindow"

on my close button I've defined Command and Command Parameter like

CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"

then in my view model

private ICommand _cancelCommand;        
public ICommand CancelCommand       
{
   get          
     {
        if (_cancelCommand == null)
           {
              _cancelCommand = new DelegateCommand<Window>(
                    x =>
                    {
                        x?.Close();
                    });
            }

            return _cancelCommand;          
     }      
}
查看更多
Fickle 薄情
4楼-- · 2019-01-17 19:55

Watch out for trendy paradigms. MVVM can be useful, but you really shouldn't treat it as a rigid set of rules. Use your own judgement, and when it doesn't make sense - don't use it.

The solutions provided here (with the exception of @RV1987's solution) are a very good example of things getting out of hands. You are replacing a single Close() call with such a huge amount of code, for what purpose? You gain absolutely nothing from moving the closing code from the view to the view-model. The only thing you gain is room for more bugs.

Now, I'm not saying MVVM is to be ignored. Quite the contrary, it can be very useful. Just don't over do it.

查看更多
看我几分像从前
5楼-- · 2019-01-17 19:58

This solution is quick and easy. Downside is that there is some coupling between the layers.

In your viewmodel:

public class MyWindowViewModel: ViewModelBase
{


    public Command.StandardCommand CloseCommand
    {
        get
        {
            return new Command.StandardCommand(Close);
        }
    }
    public void Close()
    {
        foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
            }
        }
    }
}
查看更多
三岁会撩人
6楼-- · 2019-01-17 20:01

using MVVM-light toolkit:

In the ViewModel:

 public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

And in the View:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});
查看更多
Root(大扎)
7楼-- · 2019-01-17 20:04

I personally use a very simple approach: for every ViewModel that is related to a closeable View, I created a base ViewModel like this following example:

public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }
}

Then in your ViewModel that inherits from CloseableViewModel, simply call this.OnClosingRequest(); for the Close command.

In the view:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}
查看更多
登录 后发表回答