什么是 ViewModelLocator 以及它与 DataTemplate 相比有何优缺点?

发布于 2024-10-27 06:10:15 字数 138 浏览 3 评论 0原文

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

我尝试在谷歌上查找信息,但似乎有许多不同的实现,并且没有直接列出它是什么以及使用它的优点/缺点。

Can someone give me a quick summary of what a ViewModelLocator is, how it works, and what the pros/cons are for using it compared to DataTemplates?

I have tried finding info on Google but there seems to be many different implementations of it and no striaght list as to what it is and the pros/cons of using it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

2024-11-03 06:10:15

在 MVVM 中,通常的做法

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

DI 的优点

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

源自 DI 的问题

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

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

然而,在设计时,我们的代码没有运行。设计器尝试使用反射来创建 View 的实例,这意味着:

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

输入 ViewModelLocator

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

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

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

总结

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

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

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

Intro

In MVVM the usual practice is to have the Views find their ViewModels by resolving them from a dependency injection (DI) container. This happens automatically when the container is asked to provide (resolve) an instance of the View class. The container injects the ViewModel into the View by calling a constructor of the View which accepts a ViewModel parameter; this scheme is called inversion of control (IoC).

Benefits of DI

The main benefit here is that the container can be configured at run time with instructions on how to resolve the types that we request from it. This allows for greater testability by instructing it to resolve the types (Views and ViewModels) we use when our application actually runs, but instructing it differently when running the unit tests for the application. In the latter case the application will not even have a UI (it's not running; just the tests are) so the container will resolve mocks in place of the "normal" types used when the application runs.

Problems stemming from DI

So far we have seen that the DI approach allows easy testability for the application by adding an abstraction layer over the creation of application components. There is one problem with this approach: it doesn't play well with visual designers such as Microsoft Expression Blend.

The problem is that in both normal application runs and unit test runs, someone has to set up the container with instructions on what types to resolve; additionally, someone has to ask the container to resolve the Views so that the ViewModels can be injected into them.

However, in design time there is no code of ours running. The designer attempts to use reflection to create instances of our Views, which means that:

  • If the View constructor requires a ViewModel instance the designer won't be able to instantiate the View at all -- it will error out in some controlled manner
  • If the View has a parameterless constructor the View will be instantiated, but its DataContext will be null so we 'll get an "empty" view in the designer -- which is not very useful

Enter ViewModelLocator

The ViewModelLocator is an additional abstraction used like this:

  • The View itself instantiates a ViewModelLocator as part of its resources and databinds its DataContext to the ViewModel property of the locator
  • The locator somehow detects if we are in design mode
  • If not in design mode, the locator returns a ViewModel that it resolves from the DI container, as explained above
  • If in design mode, the locator returns a fixed "dummy" ViewModel using its own logic (remember: there is no container in design time!); this ViewModel typically comes prepopulated with dummy data

Of course this means that the View must have a parameterless constructor to begin with (otherwise the designer won't be able to instantiate it).

Summary

ViewModelLocator is an idiom that lets you keep the benefits of DI in your MVVM application while also allowing your code to play well with visual designers. This is sometimes called the "blendability" of your application (referring to Expression Blend).

After digesting the above, see a practical example here.

Finally, using data templates is not an alternative to using ViewModelLocator, but an alternative to using explicit View/ViewModel pairs for parts of your UI. Often you may find that there's no need to define a View for a ViewModel because you can use a data template instead.

红衣飘飘貌似仙 2024-11-03 06:10:15

@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);
    }
}

要使用它,我可以将定位器添加到 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}}">

An example implementation of @Jon's answer

I have a view model locator class. Each property is going to be an instance of the view model that I'm going to allocate on my view. I can check if the code is running in design mode or not using DesignerProperties.GetIsInDesignMode. This allows me to use a mock model during designing time and the real object when I'm running the application.

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);
    }
}

And to use it I can add my locator to App.xaml resources:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

And then to wire up your view (ex: MainView.xaml) to your viewmodel:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
北方的韩爷 2024-11-03 06:10:15

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

视图模型定位器的目的是允许您的视图实例化它(是的,视图模型定位器 = View First):

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

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

请参阅:

I don't understand why the other answers of this question wrap around the Designer.

The purpose of the View Model Locator is to allow your View to instantiate this (yes, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

instead of just this:

public void MyWindowViewModel()
{
}

by declaring this:

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

Where ViewModelLocator is class, which references a IoC and that's how it solves the MainWindowModel property it exposes.

It has nothing to do with providing Mock view models to your view. If you want that, just do

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

The View Model Locator is a wrapper around some (any) Inversion of Control container, such as Unity for example.

Refer to:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文