0

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

然后出现了来自@SpikeX 的回复,现在我很困惑。但我必须为此感谢他。我不能停止思考这个问题,因为直到现在我可能还在以错误的方式思考 MVVM。

于是我开始研究:

从 ViewModel 关闭窗口

MVVM 的基本概念——ViewModel 应该做什么?

等等...

如您所见,我不是宇宙中唯一一个关闭窗户的人ViewModel。但我真的可以这样做吗?或者我真的不应该在ViewModel. MVVM 对这个真的那么严格吗?我的解决方案真的打破了 MVVM 模式吗?

4

2 回答 2

1

好吧,是的,您的解决方案正在打破这种模式。最大的缺点是,您无法完全测试 VM 和逻辑,这会干扰窗口的关闭。但与往常一样,您必须考虑是否值得努力实施解决方法。

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

引用

<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"先声明命名空间,然后再声明 binding 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
}
于 2013-05-24T07:51:38.357 回答
1

好吧,在我看来,视图模型应该与使用它的客户端技术完全隔离。

由于您在实际Window实例上调用 close 方法,因此您需要对特定于客户端的程序集(在本例中为 WPF)的引用,这几乎使得无法将该视图模型重用于其他任何东西。

如果您想同时制作 WPF、Silverlight、Windows Phone、Windows Store App 等客户端,您将没有机会为它们都使用相同的视图模型,因为 Windows Phone 可能不知道 WPF 窗口是什么。

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

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

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

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

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

关键是视图模型不知道实际视图。一切都隐藏在接口的实现中。

于 2013-05-23T18:46:52.860 回答