什么是ViewModelLocator,与DataTemplates相比有哪些优缺点?

Rac*_*hel 109 wpf datatemplate mvvm mvvm-light viewmodellocator

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

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

Jon*_*Jon 193

介绍

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

DI的好处

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

DI产生的问题

到目前为止,我们已经看到DI方法通过在应用程序组件的创建上添加抽象层来为应用程序提供简单的可测试性.这种方法存在一个问题:它与视觉设计器(如Microsoft Expression Blend)不兼容.

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

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

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

输入ViewModelLocator

ViewModelLocator是一个额外的抽象,使用如下:

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

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

摘要

ViewModelLocator是一个成语,它可以让您在MVVM应用程序中保留DI的好处,同时还允许您的代码与视觉设计师一起使用.这有时被称为应用程序的"可混合性"(指Expression Blend).

消化了上述内容后,请参见此处的实际示例.

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

  • +1给出了很好的解释.你能进一步扩展View及其资源吗?通过参考资料,您的意思是View的属性吗?要么?你有这个模式的具体例子的链接吗? (4认同)
  • 极具误导性的答案。View Model Locator 的主要目的不是为设计者提供虚拟数据。您可以通过指定 `d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"` 轻松完成此操作。Locator 的目的是在视图上实际启用 DI,因为 WPF 在提供它方面非常糟糕。示例:您有一个主窗口,可打开一些对话框窗口。要以通常的方式解决对话窗口上的 DI,您需要将其作为对主窗口的依赖项传递!使用视图定位器可以避免这种情况。 (2认同)

Bru*_*oLM 8

@ 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,我可以将我的定位器添加到App.xaml资源:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
Run Code Online (Sandbox Code Playgroud)

然后将您的视图(例如:MainView.xaml)连接到您的viewmodel:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
Run Code Online (Sandbox Code Playgroud)


hya*_*kov 7

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

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

public void MyWindowViewModel(IService someService)
{
}
Run Code Online (Sandbox Code Playgroud)

而不仅仅是这个:

public void MyWindowViewModel()
{
}
Run Code Online (Sandbox Code Playgroud)

通过声明:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"
Run Code Online (Sandbox Code Playgroud)

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

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

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
Run Code Online (Sandbox Code Playgroud)

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

参考: