Ответ 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, потому что вместо этого вы можете использовать шаблон данных.