【发布时间】:2011-07-24 15:20:12
【问题描述】:
有人可以快速总结一下 ViewModelLocator 是什么、它是如何工作的,以及与 DataTemplates 相比使用它的优缺点吗?
我曾尝试在 Google 上查找信息,但似乎有许多不同的实现方式,并且没有关于它是什么以及使用它的优缺点的严格列表。
【问题讨论】:
标签: wpf mvvm datatemplate mvvm-light viewmodellocator
有人可以快速总结一下 ViewModelLocator 是什么、它是如何工作的,以及与 DataTemplates 相比使用它的优缺点吗?
我曾尝试在 Google 上查找信息,但似乎有许多不同的实现方式,并且没有关于它是什么以及使用它的优缺点的严格列表。
【问题讨论】:
标签: wpf mvvm datatemplate mvvm-light viewmodellocator
我不明白为什么这个问题的其他答案都围绕着设计师。
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}"
视图模型定位器是一些(任何)控制反转容器的包装器,例如 Unity。
参考:
【讨论】:
@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}}">
【讨论】:
this代替dummy有什么区别吗?
在 MVVM 中,通常的做法是让视图通过从 dependency injection (DI) 容器中解析它们来找到它们的视图模型。当要求容器提供(解析) View 类的实例时,这会自动发生。容器通过调用接受 ViewModel 参数的 View 的构造函数将 ViewModel 注入到 View 中;这种方案称为控制反转 (IoC)。
这里的主要好处是容器可以在运行时配置,并提供有关如何解析我们向其请求的类型的说明。这通过指示它解析我们在应用程序实际运行时使用的类型(视图和视图模型)来提高可测试性,但在运行应用程序的单元测试时以不同的方式指示它。在后一种情况下,应用程序甚至没有 UI(它没有运行;只是测试在运行),因此容器将解析 mocks 来代替应用程序运行时使用的“正常”类型。
到目前为止,我们已经看到 DI 方法通过在应用程序组件的创建上添加一个抽象层,可以轻松地对应用程序进行测试。这种方法存在一个问题:它不适用于视觉设计师,例如 Microsoft Expression Blend。
问题在于,在正常的应用程序运行和单元测试运行中,都必须有人设置容器,说明要解决的类型;此外,有人必须要求容器来解析视图,以便可以将 ViewModel 注入其中。
但是,在设计时没有我们的代码在运行。设计者尝试使用反射来创建我们视图的实例,这意味着:
DataContext 将是 null,因此我们将在设计器中获得一个“空”视图 -- 这不是很有用ViewModelLocator 是一个额外的抽象,像这样使用:
当然这意味着 View 必须有一个无参数的构造函数开始(否则设计者将无法实例化它)。
ViewModelLocator 是一种习惯用法,它可以让您在 MVVM 应用程序中保留 DI 的优势,同时还可以让您的代码与可视化设计器很好地配合使用。这有时被称为应用程序的“可混合性”(指的是 Expression Blend)。
消化以上内容后,看一个实际例子here。
最后,使用数据模板不是使用 ViewModelLocator 的替代方法,而是在部分 UI 中使用显式 View/ViewModel 对的替代方法。通常,您可能会发现不需要为 ViewModel 定义视图,因为您可以使用数据模板来代替。
【讨论】:
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}" 轻松做到这一点。 Locator 的目的是在 Views 上实际启用 DI,因为 WPF 在提供它方面非常糟糕。示例:您有一个主窗口,它打开了一些对话框窗口。要以通常的方式解决对话框窗口上的 DI,您需要将其作为对主窗口的依赖项传递! View Locator 可以避免这种情况。