Что такое ViewModelLocator и каковы его плюсы и минусы по сравнению с DataTemplates?

Может кто-нибудь дать мне краткое описание того, что такое ViewModelLocator, как он работает и какие плюсы/минусы для его использования по сравнению с DataTemplates?

Я пробовал находить информацию о Google, но, похоже, это много разных реализаций, и ни один из списков stryaght относительно того, что это такое, и плюсы/минусы использования.

Ответ 1

Введение

В MVVM обычная практика заключается в том, чтобы представления находили свои ViewModels, разрешая их из контейнера dependication injection (DI). Это происходит автоматически, когда контейнеру предлагается предоставить (разрешить) экземпляр класса View. Контейнер вставляет ViewModel в представление, вызывая конструктор представления, который принимает параметр ViewModel; эта схема называется инверсией управления (IoC).

Преимущества DI

Основное преимущество здесь заключается в том, что контейнер можно настроить во время выполнения с инструкциями о том, как разрешать типы, которые мы запрашиваем от него. Это позволяет повысить степень проверки, инструктируя его разрешать типы (Views и ViewModels), которые мы используем, когда наше приложение действительно выполняется, но инструктируя его по-разному при выполнении модульных тестов для приложения. В последнем случае приложение даже не будет иметь пользовательский интерфейс (он не работает, просто тесты), поэтому контейнер разрешит mocks вместо "обычные" типы, используемые при запуске приложения.

Проблемы, связанные с DI

До сих пор мы видели, что подход DI позволяет легко тестировать приложение, добавляя слой абстракции над созданием компонентов приложения. Существует одна проблема с этим подходом: он плохо работает с визуальными дизайнерами, такими как Microsoft Expression Blend.

Проблема заключается в том, что при запуске обычных приложений и unit test кто-то должен настроить контейнер с инструкциями о том, какие типы должны быть разрешены; Кроме того, кто-то должен попросить контейнер разрешить представления, чтобы в них можно было вставлять ViewModels.

Однако во время разработки кода не работает наш код. Дизайнер пытается использовать отражение для создания экземпляров наших представлений, что означает, что:

  • Если конструктору вида требуется экземпляр ViewModel, конструктор не сможет создать экземпляр представления вообще - он будет некорректно выходить из строя.
  • Если View имеет конструктор без параметров, будет создан экземпляр View, но его DataContext будет null, поэтому мы получим "пустой" вид в дизайнере, что не очень полезно

Введите ViewModelLocator

ViewModelLocator - это дополнительная абстракция, используемая следующим образом:

  • Сам вид создает экземпляр ViewModelLocator как часть его ресурсов и привязывает его DataContext к свойству ViewModel локатора
  • Локатор каким-то образом обнаруживает, находятся ли мы в режиме разработки
  • Если нет в режиме разработки, локатор возвращает ViewModel, который он разрешает из контейнера DI, как описано выше.
  • Если в режиме разработки локатор возвращает фиксированный "dummy" ViewModel, используя свою собственную логику (помните: во время разработки нет контейнера!); этот ViewModel обычно заносится в фиктивные данные

Конечно, это означает, что для представления View должен иметь конструктор без параметров (в противном случае конструктор не сможет его создать).

Резюме

ViewModelLocator - это идиома, которая позволяет вам сохранять преимущества DI в вашем приложении MVVM, а также позволяя вашему коду хорошо играть с визуальными дизайнерами. Иногда это называется "смешиваемостью" вашего приложения (ссылка на выражение).

После переваривания выше см. практический пример здесь.

Наконец, использование шаблонов данных не является альтернативой использованию ViewModelLocator, а является альтернативой использованию явных пар View/ViewModel для частей вашего пользовательского интерфейса. Часто вы можете обнаружить, что нет необходимости определять представление для ViewModel, потому что вместо этого вы можете использовать шаблон данных.

Ответ 2

Пример реализации @Jon 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}}">

Ответ 3

Я не понимаю, почему другие ответы этого вопроса обернутся вокруг Дизайнера.

Цель View Model Locator - позволить вашему представлению создать экземпляр (да, 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}"

Локатор View Model является оболочкой вокруг некоторого (любого) Inversion of Control контейнера, например Unity.

Обратитесь к: