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

时间:2012-08-14 04:32:00

标签: c# wpf mvvm relaycommand

所以我的第一次尝试完成了后面的代码,现在我正在尝试重构我的代码以使用MVVM模式,遵循MVVM in the box信息的指导。

我已经创建了一个viewmodel类来匹配我的视图类,我正在将代码从代码中移出到viewmodel中,从命令开始。

我的第一个障碍是尝试实现一个“关闭”按钮,如果数据未被修改则关闭窗口。我已经装配了一个CloseCommand来替换'onClick'方法,除了代码试图运行this.Close()的地方外,一切都很好。显然,由于代码已从窗口移动到普通类,因此“this”不是窗口,因此不可关闭。但是,根据MVVM,viewmodel不知道该视图,因此我无法调用view.Close()

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

13 个答案:

答案 0 :(得分:58)

我个人使用一种非常简单的方法:对于与可关闭View相关的每个ViewModel,我创建了一个基本ViewModel,如下例所示:

public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

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

然后在继承自CloseableViewModel的ViewModel中,只需为this.OnClosingRequest();命令调用Close

在视图中:

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

答案 1 :(得分:29)

您无需将View实例传递给ViewModel图层。您可以像这样访问主窗口 -

Application.Current.MainWindow.Close()

我发现在ViewModel类中访问主窗口没有问题,如上所述。根据MVVM原则,View和ViewModel之间不应该存在紧密耦合,即它们应该忽略其他操作。在这里,我们没有从View向ViewModel传递任何内容。如果您想寻找其他选项,这可能会对您有所帮助 - Close window using MVVM

答案 2 :(得分:25)

我点击按钮时从视图模型关闭窗口的解决方案如下:

在视图模型中

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

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

在视图中,设置如下

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />

答案 3 :(得分:12)

我是通过创建一个名为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);
    }
}

然后在窗口标记

中将此内容写入XAML
WindowActions:DialogCloser.DialogResult="{Binding Close}"

最后在ViewModel

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

如果将Close更改为true,则窗口将关闭

Close = True;

答案 4 :(得分:10)

注意时尚范例。 MVVM可能很有用,但你真的不应该将它视为一套严格的规则。使用你自己的判断,当它没有意义时 - 不要使用它。

此处提供的解决方案(@ RV1987的解决方案除外)是非常好的举例失控的例子。您正在用如此大量的代码替换单个Close()调用,目的是什么?通过将结束代码从视图移动到视图模型,您完全没有任何好处。你获得的唯一的东西是更多的错误的空间。

现在,我不是说要忽略MVVM。恰恰相反,它非常有用。只是不要过度。

答案 5 :(得分:6)

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

ViewModel代码

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

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

这是XAML查看代码

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

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

答案 6 :(得分:5)

此解决方案快速简便。缺点是层之间存在一些耦合。

在你的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)

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

在viewmodel中:

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

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

在窗口构造函数中注册消息:

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});

答案 8 :(得分:2)

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

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

答案 9 :(得分:2)

使用MVVM-light工具包:

在ViewModel中:

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

在视图中:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});

答案 10 :(得分:0)

这取自ken2k的答案(谢谢!),只需将CloseCommand添加到基座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;
    }
}

您的视图模型,继承它

public class MyViewModel : CloseableViewModel

然后在你看来

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

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}

答案 11 :(得分:0)

如果有办法,请检查

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

简短说明

  1. 从INotifyPropertyChanged
  2. 派生您的ViewModel
  3. 在ViewModel中创建一个可观察的属性CloseDialog,每当要关闭对话框时,请更改CloseDialog属性。
  4. 在视图中附加处理程序以进行此属性更改
  5. 现在你差不多完成了。在事件处理程序中使DialogResult = true

答案 12 :(得分:0)

首先给你的窗口命名为

x:Name="AboutViewWindow"

在我的关闭按钮上我已经定义了命令和命令参数,如

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

然后在我的视图模型中

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

            return _cancelCommand;          
     }      
}