最近,我回答了一个问题How to Bind to window's close button the X-button,我认为这是一个MVVM解决方案。请不要关注那里的实际问题,因为那不是什么困扰我。在那种特殊情况下,我甚至不会使用我的解决方案。我肯定会使用@ChrisW的解决方案。
然后@SpikeX的响应出现了,现在我很困惑。但我必须为此感谢他。我无法停止思考,因为直到现在我才可能以错误的方式思考MVVM。
所以我开始研究:
Basic concepts of MVVM— what should a ViewModel do?
依旧......
正如你所看到的,我并不是宇宙中唯一一个从ViewModel
关闭窗口的人。但我真的可以这样做吗?或者我不应该在ViewModel
中使用窗口。 MVVM真的如此严格吗?真的是我的解决方案打破MVVM模式吗?
答案 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
}