是否允许ViewModel关闭窗口?

时间:2013-05-23 16:31:54

标签: wpf mvvm design-patterns

最近,我回答了一个问题How to Bind to window's close button the X-button,我认为这是一个MVVM解决方案。请不要关注那里的实际问题,因为那不是什么困扰我。在那种特殊情况下,我甚至不会使用我的解决方案我肯定会使用@ChrisW的解决方案。

然后@SpikeX的响应出现了,现在我很困惑。但我必须为此感谢他。我无法停止思考,因为直到现在我才可能以错误的方式思考MVVM。

所以我开始研究:

Close Window from ViewModel

Basic concepts of MVVM— what should a ViewModel do?

依旧......

正如你所看到的,我并不是宇宙中唯一一个从ViewModel关闭窗口的人。但我真的可以这样做吗?或者我不应该在ViewModel中使用窗口。 MVVM真的如此严格吗?真的是我的解决方案打破MVVM模式吗?

2 个答案:

答案 0 :(得分:1)

嗯,在我看来,viewmodel应该与使用它的客户端技术完全隔离。

由于您在实际的Window实例上调用close方法,因此需要引用特定于客户端的程序集(在本例中为WPF),这几乎使得无法将该viewmodel重用于其他任何内容。 / p>

如果您想同时制作WPF,Silverlight,Windows Phone,Windows应用商店等客户端,您将无法使用相同的视图模型,因为Windows Phone可能不知道什么是WPF窗口是

此外,当您在其中引用实际视图元素时,单元测试视图模型会变得更加麻烦。

因此,不是直接引用窗口,而是可以在某种视图适配器中将其抽象出来。

如果viewmodel只知道这样的接口:

public interface IView
{
    void Show();
    void Close();
}

...您的每个客户都可以创建自己的实现,并将其注入viewmodel,以便在任何给定的客户端上做正确的事情。

关键是viewmodel不了解实际视图。一切都隐藏在界面的实现中。

答案 1 :(得分:1)

嗯,是的,你的解决方案打破了模式。最大的缺点是,您无法完全测试VM和逻辑,这会干扰窗口的关闭。但一如既往,您必须考虑,如果值得努力实施变通方法。

因此,如果你真的想坚持MVVM,你可以使用我在第一个链接SO post中发布的解决方案。我在这里复制了帖子的基本部分。

<强>引用

<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" 
        xmlns:hlp="clr-namespace:AC.Frontend.Helper"
        MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterScreen" Title="{Binding Title}"
        hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
        Language="{Binding UiCulture, Source={StaticResource Strings}}">
        <!-- A lot more stuff here -->
</Window>

如您所见,我首先声明命名空间xmlns:hlp="clr-namespace:AC.Frontend.Helper",然后声明绑定hlp:AttachedProperties.DialogResult="{Binding DialogResult}"

[...]

public class AttachedProperties
{
    #region DialogResult

    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var wnd = d as Window;
        if (wnd == null)
            return;

        wnd.DialogResult = (bool?) e.NewValue;
    }

    public static bool? GetDialogResult(DependencyObject dp)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        return (bool?)dp.GetValue(DialogResultProperty);
    }

    public static void SetDialogResult(DependencyObject dp, object value)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        dp.SetValue(DialogResultProperty, value);
    }

    #endregion
}

<强> /报价

您唯一需要的是VM这样才能使我的解决方案有效。

public class WindowVm : ViewModelBase // base class implementing INotifyPropertyChanged
{
    private bool? _dialogResult;
    public bool? DialogResult
    {
        get { return _dialogResult; }
        set 
        {
             _dialogResult = value;
             RaisePropertyChanged(() => DialogResult);
        }
    }

    //... many other properties
}