Answers:
在MVVM中,通常的做法是通过从依赖项注入(DI)容器中解析视图来使视图找到其ViewModel 。当要求容器提供(解析)View类的实例时,这会自动发生。容器通过调用接受ViewModel参数的View的构造函数将 ViewModel注入到View中。这种方案称为控制反转(IoC)。
这里的主要好处是可以在运行时配置容器,其中包含有关如何解析我们向其请求的类型的说明。通过指示它解析我们在应用程序实际运行时使用的类型(视图和ViewModels),从而提供了更高的可测试性,但在为应用程序运行单元测试时以不同的方式指示它。在后一种情况下,应用程序甚至没有UI(它没有在运行;只是测试而已),因此容器将解析模拟,以代替在应用程序运行时使用的“常规”类型。
到目前为止,我们已经看到,DI方法通过在创建应用程序组件的过程中添加抽象层,使应用程序易于测试。这种方法有一个问题:它不适用于Microsoft Expression Blend等视觉设计器。
问题在于,在正常的应用程序运行和单元测试运行中,都必须设置容器以说明要解决的类型;另外,必须有人要求容器解析视图,以便可以将ViewModels注入其中。
但是,在设计时没有运行代码。设计器尝试使用反射来创建我们的View实例,这意味着:
DataContext
会null
,所以我们会得到在设计一个‘空’的观点-这是不是很实用ViewModelLocator是这样使用的附加抽象:
当然,这意味着View必须具有一个无参数的构造函数(否则设计器将无法实例化它)。
ViewModelLocator是一个惯用语,可让您在MVVM应用程序中保留DI的优点,同时还使您的代码可与可视设计人员很好地配合使用。有时这称为应用程序的“可混合性”(指Expression Blend)。
消化完上述内容后,请参见此处的实际示例。
最后,使用数据模板不是使用ViewModelLocator的替代方法,而是使用UI的显式View / ViewModel对的替代方法。通常,您可能会发现不需要为ViewModel定义View,因为您可以改用数据模板。
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
。定位器的目的是在视图上实际启用DI,因为WPF很难提供它。示例:您有一个主窗口,它打开了一些对话框窗口。要以通常的方式在对话框窗口上解决DI,您需要将其作为对主窗口的依赖项传递!使用“视图定位器”可以避免这种情况。
@Jon答案的示例实现
我有一个视图模型定位器类。每个属性都将是我将在视图上分配的视图模型的实例。我可以检查代码是否在设计模式下运行或不使用DesignerProperties.GetIsInDesignMode
。这使我可以在设计时使用模拟模型,并在运行应用程序时使用真实对象。
public class ViewModelLocator
{
private DependencyObject dummy = new DependencyObject();
public IMainViewModel MainViewModel
{
get
{
if (IsInDesignMode())
{
return new MockMainViewModel();
}
return MyIoC.Container.GetExportedValue<IMainViewModel>();
}
}
// returns true if editing .xaml file in VS for example
private bool IsInDesignMode()
{
return DesignerProperties.GetIsInDesignMode(dummy);
}
}
要使用它,我可以将定位器添加到App.xaml
资源中:
xmlns:core="clr-namespace:MyViewModelLocatorNamespace"
<Application.Resources>
<core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
然后将您的视图(例如:MainView.xaml)连接到您的视图模型:
<Window ...
DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
this
代替有什么区别dummy
吗?
我不明白为什么这个问题的其他答案围绕着Designer。
View Model Locator的目的是允许您的View实例化它(是的,View Model Locator = View First):
public void MyWindowViewModel(IService someService)
{
}
不仅仅是这样:
public void MyWindowViewModel()
{
}
通过声明:
DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"
ViewModelLocator
class 在哪里,它引用了一个IoC,这就是它解决MainWindowModel
其公开的属性的方式。
与向您的视图提供Mock视图模型无关。如果你想要的话,就去做
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
View Model Locator是Control容器的某些(任何)Inversion的包装,例如Unity。
参考: