Вопрос:
Может кто-нибудь дать мне краткое описание того, что такое ViewModelLocator, как он работает и какие плюсы/минусы для его использования по сравнению с DataTemplates?
Я пробовал находить информацию о Google, но, похоже, это много разных реализаций, и ни один из списков stryaght относительно того, что это такое, и плюсы/минусы использования.
Лучший ответ:Содержание
Введение
В 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, потому что вместо этого вы можете использовать шаблон данных.
Ответ №1
Пример реализации @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}}»> Ответ №2
Я не понимаю, почему другие ответы этого вопроса обернутся вокруг Дизайнера.
Цель 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.
Обратитесь к:
- Как обрабатывать инъекцию зависимостей в приложении WPF/MVVM
- http://blog.qmatteoq.com/the-mvvm-pattern-dependency-injection/