【问题标题】:What is a ViewModelLocator and what are its pros/cons compared to DataTemplates?什么是 ViewModelLocator,与 DataTemplates 相比,它的优缺点是什么?
【发布时间】:2011-07-24 15:20:12
【问题描述】:

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

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

【问题讨论】:

    标签: wpf mvvm datatemplate mvvm-light viewmodellocator


    【解决方案1】:

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

    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。

    参考:

    【讨论】:

    • 视图模型定位器不需要容器,用户决定如何通过配置解析视图模型,您可以使用容器或自己新建一个类型。因此,您可以进行基于约定的视图模型位置,例如,而不是在某个容器中预先注册所有视图和视图模型。
    • 你说得对,“我不明白为什么其他答案 [...] 围绕着设计师”+1,但是定位器的重点是从视图中删除关于如何创建视图模型的任何知识,使视图独立于该实例化,将其留给定位器。定位器将能够提供不同风格的视图模型,也许是通过定位器将管理的插件添加的一些自定义(以及设计时的特定插件)。视图将清除任何定位视图模型正确版本的过程,这对 SoC 确实有好处。
    【解决方案2】:

    @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有什么区别吗?
    【解决方案3】:

    简介

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

    DI的好处

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

    DI 引起的问题

    到目前为止,我们已经看到 DI 方法通过在应用程序组件的创建上添加一个抽象层,可以轻松地对应用程序进行测试。这种方法存在一个问题:它不适用于视觉设计师,例如 Microsoft Expression Blend。

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

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

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

    输入 ViewModelLocator

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

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

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

    总结

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

    消化以上内容后,看一个实际例子here

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

    【讨论】:

    • +1 以获得很好的解释。您能否进一步扩展视图及其资源?通过资源,您是指视图的属性吗?要么?你有这个模式的具体例子的链接吗?
    • @MetroSmurf:您的链接在摘要部分。
    • 谢谢。使用 ViewModelLocator 有什么限制吗?我对它引用静态资源这一事实有些担心——ViewModel 可以在运行时动态创建吗?是否有很多额外的代码涉及到连接一个?
    • @Rachel:摘要中的同一链接应该通过实际示例回答这些问题。
    • 极具误导性的答案。 View Model Locator 的主要目的不是向设计人员提供虚拟数据。您可以通过指定d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}" 轻松做到这一点。 Locator 的目的是在 Views 上实际启用 DI,因为 WPF 在提供它方面非常糟糕。示例:您有一个主窗口,它打开了一些对话框窗口。要以通常的方式解决对话框窗口上的 DI,您需要将其作为对主窗口的依赖项传递! View Locator 可以避免这种情况。
    猜你喜欢
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 2019-09-07
    • 2016-02-27
    • 1970-01-01
    • 2011-03-29
    • 2016-03-26
    • 1970-01-01
    相关资源
    最近更新 更多