什么是 ViewModelLocator? 与 DataTemplate 相比,它的优缺点是什么?

谁能给我一个关于 ViewModelLocator 是什么,它是如何工作的,以及与 DataTemplate 相比使用它的优缺点的快速总结?

I have tried finding info on Google but there seems to be many different implementations of it and no striaght list as to what it is and the pros/cons of using it.

49107 次浏览

介绍

在 MVVM 中,通常的做法是让视图通过从 依赖注入(DI)容器中解析视图模型来找到它们。当要求容器提供(解析) View 类的实例时,会自动发生这种情况。容器 注射通过调用接受 ViewModel 参数的 View 的构造函数将 ViewModel 放入 View 中; 这个方案称为 控制反转(IoC)。

直接投资的好处

这里的主要好处是,可以对容器进行 在运行时配置,并提供有关如何解析我们从容器请求的类型的说明。通过指示它解析应用程序实际运行时使用的类型(视图和视图模型) ,但指示它在运行应用程序的单元测试时使用不同的类型,从而提高了可测试性。在后一种情况下,应用程序甚至没有 UI (它没有运行,只有测试在运行) ,因此容器将解析 嘲笑,以取代应用程序运行时使用的“普通”类型。

来自 DI 的问题

到目前为止,我们已经看到 DI 方法通过为应用程序组件的创建添加一个抽象层,使应用程序的可测试性变得容易。这种方法有一个问题: it doesn't play well with visual designers,比如 MicrosoftExpressionBlend。

问题在于,在正常的应用程序运行和单元测试运行中,必须有人对容器进行 准备好了操作,并指示要解析哪些类型; 此外,必须有人对容器进行 问吧操作来解析视图,以便将 ViewModel 注入到视图模型中。

但是,设计器尝试使用反射来创建视图的实例,这意味着:

  • 如果 View 构造函数需要一个 ViewModel 实例,那么设计器将根本无法实例化 View ——它将以某种受控的方式出错
  • 如果 View 有一个无参数的构造函数,那么 View 将被实例化,但是它的 DataContext将是 null,因此我们将在设计器中得到一个“空”视图——这不是很有用

输入 ViewModelLocator

ViewModelLocator 是一个额外的抽象,如下所示:

  • View 本身将 ViewModelLocator 实例化为其 资源的一部分,并将其 DataContext 绑定到定位器的 ViewModel 属性
  • 定位器以某种方式 检测我们是否处于设计模式
  • If not in design mode, the locator returns a ViewModel that it resolves from the DI container, as explained above
  • 如果在设计模式下,定位器使用自己的逻辑返回一个固定的“虚拟”ViewModel (记住: 在设计时没有容器!)这个 ViewModel 通常预先填充了虚拟数据

当然,这意味着视图首先必须有一个无参数的构造函数(否则设计器将无法实例化它)。

摘要

ViewModelLocator 是一个习惯用法,它可以让您在 MVVM 应用程序中保留 DI 的优点,同时也允许您的代码与可视化设计器很好地配合。这有时被称为应用程序的“可混合性”(参考 ExpressionBlend)。

在理解了上面的内容之后,请参阅一个实际的例子 here

最后,使用数据模板不是使用 ViewModelLocator 的替代方法,而是在 UI 的某些部分使用显式的 View/ViewModel 对的替代方法。通常您会发现不需要为 ViewModel 定义 View,因为您可以使用数据模板。

An example implementation of @ 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}}">

我不明白为什么这个问题的其他答案包围设计师。

The purpose of the View Model Locator is to allow your View to instantiate this (yes, 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}"

视图模型定位器是一个包装一些(任何)控制反转容器的包装器,例如 Unity。

请参阅: