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

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

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

介绍

在MVVM中,通常的做法是通过从dependency injection (DI)容器中parsingView来findViewModel。 当容器被要求提供(parsing)View类的一个实例时,会自动发生。 容器通过调用视图的构造函数 ViewModel注入到视图中,该视图接受ViewModel参数; 这个scheme被称为控制反转 (IoC)。

DI的好处

这里的主要好处是可以在运行时configuration容器并指示如何解决我们从中请求的types。 这允许通过指示它来parsing我们的应用程序实际运行时使用的types(视图和视图模型),但在运行应用程序的unit testing时指示它不同,从而提高了可testing性。 在后一种情况下,应用程序甚至没有UI(它不运行;只是testing),所以容器将parsing模拟代替应用程序运行时使用的“正常”types。

来自DI的问题

到目前为止,我们已经看到,通过在创build应用程序组件时添加抽象层,DI方法允许应用程序的易testing性。 这种方法存在一个问题: 它与 Microsoft Expression Blend等可视化devise器不兼容

问题是,在正常的应用程序运行和unit testing运行中,有人必须设置容器,指出要解决的types; 此外,有人必须要求容器parsing视图,以便ViewModels可以注入到视图中。

但是, 在devise的时候没有我们的代码运行 。 devise者试图使用reflection来创build我们视图的实例,这意味着:

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

inputViewModelLocator

ViewModelLocator是这样一个额外的抽象:

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

当然这意味着View必须有一个无参数的构造函数(否则devise者将不能实例化它)。

概要

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

消化以上之后, 在这里看一个实际的例子。

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

@ Jon的答案的一个示例实现

我有一个视图模型定位器类。 每个属性将成为我将在视图上分配的视图模型的一个实例。 我可以检查代码是否在devise模式下运行或不使用DesignerProperties.GetIsInDesignMode 。 这使我可以在devise时使用模拟模型,在运行应用程序时使用真实对象。

 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}}"> 

我不明白为什么这个问题的其他答案环绕devise师。

视图模型定位器的目的是让您的视图实例化(是的,视图模型定位器=视图第一):

 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}" 

View Model Locator是一些(任何)Inversion of Control容器的封装,例如Unity。

参考:

Interesting Posts