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条回答
smile是对你的礼貌
2楼-- · 2019-01-17 20:05

You don't need to pass the View instance to your ViewModel layer. You can access the main window like this -

Application.Current.MainWindow.Close()

I see no issue in accessing your main window in ViewModel class as stated above. As per MVVM principle there should not be tight coupling between your View and ViewModel i.e. they should work be oblivious of others operation. Here, we are not passing anything to ViewModel from View. If you want to look for other options this might help you - Close window using MVVM

查看更多
小情绪 Triste *
3楼-- · 2019-01-17 20:05

MVVM-light with a custom message notification to avoid the window to process every notificationmessage

In the viewmodel:

public class CloseDialogMessage : NotificationMessage
{
    public CloseDialogMessage(object sender) : base(sender, "") { }
}

private void OnClose()
{
    Messenger.Default.Send(new CloseDialogMessage(this));
}

Register the message in the window constructor:

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});
查看更多
Bombasti
4楼-- · 2019-01-17 20:10

This is taken from ken2k answer (thanks!), just adding the CloseCommand also to the base CloseableViewModel.

public class CloseableViewModel
{
    public CloseableViewModel()
    {
        CloseCommand = new RelayCommand(this.OnClosingRequest);
    }

    public event EventHandler ClosingRequest;

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

    public RelayCommand CloseCommand
    {
        get;
        private set;
    }
}

Your view model, inherits it

public class MyViewModel : CloseableViewModel

Then on you view

public MyView()
{
    var viewModel = new StudyDataStructureViewModel(studyId);
    this.DataContext = viewModel;

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}
查看更多
聊天终结者
5楼-- · 2019-01-17 20:11

My solution to close a window from view model while clicking a button is as follows:

In view model

public RelayCommand CloseWindow;
Constructor()
{
    CloseWindow = new RelayCommand(CloseWin);
}

public void CloseWin(object obj)
{
    Window win = obj as Window;
    win.Close();
}

In View, set as follows

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
查看更多
趁早两清
6楼-- · 2019-01-17 20:11

I do it by creating a attached property called DialogResult:

public static class DialogCloser
{
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window != null && (bool?)e.NewValue == true) 
                window.Close();
    }

    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}

then write this to you XAML, in the window tag

WindowActions:DialogCloser.DialogResult="{Binding Close}"

finally in the ViewModel

    private bool _close;
    public bool Close
    {
        get { return _close; }
        set
        {
            if (_close == value)
                return;
            _close = value;
            NotifyPropertyChanged("Close");
        }
    }

if you change the Close to true, the window will be closed

Close = True;
查看更多
叛逆
7楼-- · 2019-01-17 20:11

This is very similar to eoldre's answer. It's functionally the same in that it looks through the same Windows collection for a window that has the view model as its datacontext; but I've used a RelayCommand and some LINQ to achieve the same result.

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}
查看更多
登录 后发表回答