有人能给我简要介绍一下什么是ViewModelLocator、它的工作原理以及与使用DataTemplates相比使用它的优缺点吗?
我尝试在Google上找到相关信息,但似乎有很多不同的实现方式,并没有明确列出它是什么以及使用它的优缺点。
有人能给我简要介绍一下什么是ViewModelLocator、它的工作原理以及与使用DataTemplates相比使用它的优缺点吗?
我尝试在Google上找到相关信息,但似乎有很多不同的实现方式,并没有明确列出它是什么以及使用它的优缺点。
MVVM中通常的做法是通过从依赖注入(DI)容器中解析它们来使视图找到它们的ViewModels。当容器被要求提供(解析)View类的实例时,这会自动发生。容器通过调用接受ViewModel参数的View构造函数将ViewModel注入View中;这个方案被称为控制反转(IoC)。
主要的好处在于,容器可以在运行时配置,以指示如何解析我们从中请求的类型。这允许通过指示它在应用程序实际运行时使用的类型(Views和ViewModels)来解析类型,但在运行应用程序的单元测试时,通过不同的指示来进行更大的可测试性。在后一种情况下,应用程序甚至没有UI(它没有运行;只有测试在运行),因此容器将在应用程序运行时使用的“正常”类型的位置上解析模拟。
到目前为止,我们已经看到,DI方法通过在应用程序组件的创建上添加抽象层来使应用程序易于测试。这种方法存在一个问题:它与Microsoft Expression Blend等可视化设计工具不兼容。
问题在于在正常应用程序运行和单元测试运行中,必须有人使用指令设置容器,并要求容器解析视图以便将ViewModel注入其中。然而,在设计时没有我们的代码运行。设计师试图使用反射创建我们的视图实例,这意味着:当然,这意味着视图必须首先具有一个无参数构造函数(否则设计师将无法实例化它)。
ViewModelLocator是一种习惯用语,它让你在MVVM应用程序中保持DI的好处,同时还允许你的代码与可视化设计师良好地协作。这有时被称为应用程序的“混合能力”(指Expression Blend)。
阅读以上内容后,可以查看一个实际的示例here。
最后,使用数据模板不是使用ViewModelLocator的替代方法,而是UI部分使用显式View/ViewModel对的替代方法。通常,您可能会发现没有必要为ViewModel定义一个View,因为您可以使用数据模板。
一个实现@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
有什么区别吗? - Sebastian Xawery Wiśniowiecki我不理解为什么这个问题的其他答案会涉及到设计师。
视图模型定位器的目的是允许您的视图实例化它(是的,视图模型定位器=视图优先):
public void MyWindowViewModel(IService someService)
{
}
不仅仅是这样:
public void MyWindowViewModel()
{
}
DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"
这里提到的ViewModelLocator
是一个类,它引用了一个IoC容器来解决它所暴露的MainWindowModel
属性。
它与为视图提供模拟视图模型无关。如果需要这样做,只需执行以下操作:
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
轻松实现这一点。Locator 的目的是实际上在视图上启用 DI,因为 WPF 在提供 DI 方面非常糟糕。例如:您有一个打开某些对话框窗口的主窗口。要以通常的方式解决对话框窗口上的 DI,您需要将其作为依赖项传递给主窗口!这可以通过 View Locator 避免。 - hyankov