116

有人可以快速总结一下 ViewModelLocator 是什么,它是如何工作的,以及与 DataTemplates 相比使用它的优点/缺点是什么?

我曾尝试在 Google 上查找信息,但似乎有许多不同的实现方式,并且没有关于它是什么以及使用它的优缺点的严格列表。

4

3 回答 3

214

介绍

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

直接投资的好处

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

DI引起的问题

到目前为止,我们已经看到 DI 方法通过在应用程序组件的创建上添加一个抽象层,从而为应用程序提供了简单的可测试性。这种方法有一个问题:它不能很好地与Microsoft Expression Blend 等视觉设计器配合使用。

问题在于,在正常的应用程序运行和单元测试运行中,都必须有人设置容器并说明要解决的类型;此外,必须有人要求容器解析视图,以便可以将 ViewModel 注入其中。

但是,在设计时没有我们的代码在运行。设计者尝试使用反射来创建我们的视图实例,这意味着:

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

输入 ViewModelLocator

ViewModelLocator 是一个额外的抽象,像这样使用:

  • View 本身将 ViewModelLocator 实例化为其资源的一部分,并将其 DataContext 数据绑定到定位器的 ViewModel 属性
  • 定位器以某种方式检测我们是否处于设计模式
  • 如果不在设计模式下,定位器会返回从 DI 容器解析的 ViewModel,如上所述
  • 如果在设计模式下,定位器会使用自己的逻辑返回一个固定的“虚拟”视图模型(记住:设计时没有容器!);此 ViewModel 通常预先填充了虚拟数据

当然这意味着 View 必须有一个无参数的构造函数开始(否则设计者将无法实例化它)。

概括

ViewModelLocator 是一个习惯用法,它可以让您在 MVVM 应用程序中保留 DI 的优势,同时还可以让您的代码与视觉设计器很好地配合使用。这有时被称为应用程序的“可混合性”(指的是 Expression Blend)。

消化完上面的内容,看一个实际的例子

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

于 2011-03-28T16:49:54.627 回答
10

@Jon's answer的示例实现

我有一个视图模型定位器类。每个属性都将是我将在我的视图上分配的视图模型的一个实例。我可以检查代码是否在设计模式下运行或不使用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}}">
于 2015-03-01T02:04:34.600 回答
8

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

View Model Locator 的目的是允许您的 View 实例化它(是的,View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

而不仅仅是这个:

public void MyWindowViewModel()
{
}

通过声明:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

类在哪里ViewModelLocator,它引用了一个 IoC,这就是它解决MainWindowModel它公开的属性的方式。

它与为您的视图提供 Mock 视图模型无关。如果你想要,就做

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

View Model Locator 是一些(任何)控制反转容器的包装器,例如 Unity。

参考:

于 2016-12-31T18:40:43.893 回答