使用MVVM实现“关闭窗口”命令

时间:2022-10-22 14:56:04

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.

因此,我的第一次尝试是从代码后面做所有的事情,现在我尝试重构我的代码来使用MVVM模式,遵循框信息中的MVVM的指导。

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.

我创建了一个viewmodel类来匹配我的view类,我将代码从后面的代码移到viewmodel中,从命令开始。

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().

我遇到的第一个问题是试图实现一个“关闭”按钮,如果数据没有被修改,该按钮将关闭窗口。我安装了一个CloseCommand以替换“onClick”方法,除了代码试图运行this.Close()之外,一切正常。显然,由于代码已经从窗口移动到普通类,因此'this'不是窗口,因此不能关闭。但是,根据MVVM, viewmodel不知道视图,所以我不能调用view.Close()。

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

有人能建议我如何从viewmodel命令关闭窗口吗?

13 个解决方案

#1


26  

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

不需要将视图实例传递给ViewModel层。你可以像这样访问主窗口

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

我认为访问ViewModel类中的主窗口没有问题。根据MVVM原则,视图和视图模型之间不应该有紧密的耦合,也就是说,视图和视图模型的工作应该不受其他操作的影响。在这里,我们没有将任何内容从View传递给ViewModel。如果您想寻找其他选项,这可能有助于您-关闭窗口使用MVVM

#2


51  

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:

我个人使用一种非常简单的方法:对于与可关闭视图相关的每一个ViewModel,我都创建了一个基本的ViewModel,比如下面的例子:

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.

然后在继承自closeviewmodel的ViewModel中,只需调用this.OnClosingRequest();“关闭”命令。

In the view:

在视图:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}

#3


22  

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" />

#4


12  

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

然后在窗口标签中把这个写给XAML

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

finally in the ViewModel

最后在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,则窗口将被关闭

Close = True;

#5


10  

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.

注意时尚范型。MVVM是有用的,但是您不应该把它当作一组严格的规则。使用你自己的判断,当它没有意义时——不要使用它。

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.

这里提供的解决方案(除了@RV1987的解决方案)是一个非常好的例子。您正在用如此大量的代码替换一个Close()调用,目的是什么?将关闭代码从视图移到视图模型中,完全没有得到任何好处。你唯一能得到的就是更多的虫子。

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

现在,我并不是说MVVM应该被忽略。恰恰相反,它可能非常有用。别做过头了。

#6


5  

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();
            }
        }
    }
}

#7


3  

Here is the simplest solution and pure MVVM solution

这是最简单的解决方案和纯MVVM解决方案

ViewModel Code

视图模型代码

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

Here is XAML View Code

这是XAML视图代码

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}

#8


2  

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.

这和eoldre的答案非常相似。它在功能上是相同的,它通过相同的窗口集合查看一个窗口,该窗口有视图模型作为它的datacontext;但是我使用了RelayCommand和一些LINQ来实现相同的结果。

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}

#9


2  

using MVVM-light toolkit:

使用MVVM-light工具包:

In the ViewModel:

在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();
    }
});

#10


2  

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

mvm -light带有自定义消息通知,以避免窗口处理每个通知消息

In the viewmodel:

在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();
});

#11


0  

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

这是从ken2k答案(谢谢!)中提取的,只是在基本的CloseableViewModel中添加了CloseCommand。

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();
}

#12


0  

Given a way, Please check

如果有办法,请检查

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

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

Short Description

简短的描述

  1. Derive your ViewModel from INotifyPropertyChanged
  2. 从INotifyPropertyChanged中派生您的ViewModel
  3. Create a observable property CloseDialog in ViewModel, Change CloseDialog property whenever you want to close the dialog.
  4. 在ViewModel中创建一个可观察的属性关闭对话框,在关闭对话框时更改CloseDialog属性。
  5. Attach a Handler in View for this property change
  6. 为此属性更改附加一个处理程序
  7. Now you are almost done. In the event handler make DialogResult = true
  8. 现在你差不多做完了。在事件处理程序中,使对话框为true

#13


0  

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;          
     }      
}

#1


26  

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

不需要将视图实例传递给ViewModel层。你可以像这样访问主窗口

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

我认为访问ViewModel类中的主窗口没有问题。根据MVVM原则,视图和视图模型之间不应该有紧密的耦合,也就是说,视图和视图模型的工作应该不受其他操作的影响。在这里,我们没有将任何内容从View传递给ViewModel。如果您想寻找其他选项,这可能有助于您-关闭窗口使用MVVM

#2


51  

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:

我个人使用一种非常简单的方法:对于与可关闭视图相关的每一个ViewModel,我都创建了一个基本的ViewModel,比如下面的例子:

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.

然后在继承自closeviewmodel的ViewModel中,只需调用this.OnClosingRequest();“关闭”命令。

In the view:

在视图:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}

#3


22  

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" />

#4


12  

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

然后在窗口标签中把这个写给XAML

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

finally in the ViewModel

最后在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,则窗口将被关闭

Close = True;

#5


10  

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.

注意时尚范型。MVVM是有用的,但是您不应该把它当作一组严格的规则。使用你自己的判断,当它没有意义时——不要使用它。

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.

这里提供的解决方案(除了@RV1987的解决方案)是一个非常好的例子。您正在用如此大量的代码替换一个Close()调用,目的是什么?将关闭代码从视图移到视图模型中,完全没有得到任何好处。你唯一能得到的就是更多的虫子。

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

现在,我并不是说MVVM应该被忽略。恰恰相反,它可能非常有用。别做过头了。

#6


5  

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();
            }
        }
    }
}

#7


3  

Here is the simplest solution and pure MVVM solution

这是最简单的解决方案和纯MVVM解决方案

ViewModel Code

视图模型代码

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

Here is XAML View Code

这是XAML视图代码

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}

#8


2  

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.

这和eoldre的答案非常相似。它在功能上是相同的,它通过相同的窗口集合查看一个窗口,该窗口有视图模型作为它的datacontext;但是我使用了RelayCommand和一些LINQ来实现相同的结果。

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}

#9


2  

using MVVM-light toolkit:

使用MVVM-light工具包:

In the ViewModel:

在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();
    }
});

#10


2  

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

mvm -light带有自定义消息通知,以避免窗口处理每个通知消息

In the viewmodel:

在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();
});

#11


0  

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

这是从ken2k答案(谢谢!)中提取的,只是在基本的CloseableViewModel中添加了CloseCommand。

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();
}

#12


0  

Given a way, Please check

如果有办法,请检查

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

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

Short Description

简短的描述

  1. Derive your ViewModel from INotifyPropertyChanged
  2. 从INotifyPropertyChanged中派生您的ViewModel
  3. Create a observable property CloseDialog in ViewModel, Change CloseDialog property whenever you want to close the dialog.
  4. 在ViewModel中创建一个可观察的属性关闭对话框,在关闭对话框时更改CloseDialog属性。
  5. Attach a Handler in View for this property change
  6. 为此属性更改附加一个处理程序
  7. Now you are almost done. In the event handler make DialogResult = true
  8. 现在你差不多做完了。在事件处理程序中,使对话框为true

#13


0  

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;          
     }      
}