Что такое ViewModelLocator и каковы его плюсы / минусы по сравнению с DataTemplates?
Может кто-нибудь дать мне краткую информацию о том, что такое ViewModelLocator, как он работает и каковы плюсы и минусы его использования по сравнению с DataTemplates?
Я пытался найти информацию в Google, но, кажется, существует много разных реализаций, и нет никакого точного списка того, что это такое, и плюсы / минусы его использования.
3 ответа
вступление
В MVVM обычная практика состоит в том, чтобы Представления находили свои ViewModel путем разрешения их из контейнера внедрения зависимостей (DI). Это происходит автоматически, когда от контейнера требуется предоставить (разрешить) экземпляр класса View. Контейнер внедряет ViewModel в View, вызывая конструктор View, который принимает параметр ViewModel; эта схема называется инверсией управления (IoC).
Преимущества DI
Основным преимуществом здесь является то, что контейнер может быть настроен во время выполнения с инструкциями о том, как разрешить типы, которые мы запрашиваем у него. Это позволяет повысить тестируемость, дав ему указание разрешать типы (Views и ViewModels), которые мы используем при фактическом запуске нашего приложения, но по-разному инструктируя его при запуске модульных тестов для приложения. В последнем случае приложение даже не будет иметь пользовательского интерфейса (оно не запущено; только тесты), поэтому контейнер будет разрешать макеты вместо "обычных" типов, используемых при запуске приложения.
Проблемы, связанные с DI
До сих пор мы видели, что подход DI позволяет легко тестировать приложение, добавляя уровень абстракции над созданием компонентов приложения. У этого подхода есть одна проблема: он плохо работает с визуальными дизайнерами, такими как Microsoft Expression Blend.
Проблема заключается в том, что как при обычном запуске приложения, так и при выполнении модульного теста кто-то должен настроить контейнер с инструкциями, какие типы разрешать; Кроме того, кто-то должен попросить контейнер разрешить представления, чтобы в них могли быть внедрены модели представления.
Тем не менее, во время разработки не существует нашего кода. Дизайнер пытается использовать отражение для создания экземпляров наших представлений, что означает, что:
- Если для конструктора View требуется экземпляр ViewModel, то дизайнер вообще не сможет создать экземпляр View - он каким-либо контролируемым образом выдаст ошибку
- Если представление имеет конструктор без параметров, представление будет создано, но его
DataContext
будетnull
так что мы получим "пустое" представление в конструкторе, что не очень полезно
Введите ViewModelLocator
ViewModelLocator - это дополнительная абстракция, используемая следующим образом:
- Само представление создает экземпляр ViewModelLocator как часть его ресурсов и связывает его DataContext со свойством ViewModel локатора.
- Локатор как-то определяет, находимся ли мы в режиме разработки
- Если не в режиме разработки, локатор возвращает ViewModel, который он разрешает из контейнера DI, как объяснено выше
- В режиме разработки локатор возвращает фиксированную "фиктивную" модель представления с использованием собственной логики (помните: во время разработки контейнера нет!); эта ViewModel обычно поставляется с фиктивными данными
Конечно, это означает, что для начала у View должен быть конструктор без параметров (иначе конструктор не сможет его создать).
Резюме
ViewModelLocator - это идиома, которая позволяет вам сохранить преимущества DI в вашем приложении MVVM, а также позволяет вашему коду хорошо работать с визуальными дизайнерами. Это иногда называется "смешиваемостью" вашего приложения (имеется в виду Expression Blend).
После усвоения вышеизложенного, посмотрите практический пример здесь.
Наконец, использование шаблонов данных не является альтернативой использованию ViewModelLocator, но альтернативой использованию явных пар View/ViewModel для частей вашего пользовательского интерфейса. Часто вы можете обнаружить, что нет необходимости определять View для ViewModel, потому что вместо этого вы можете использовать шаблон данных.
Я не понимаю, почему другие ответы на этот вопрос связаны с Дизайнером.
Назначение локатора модели представлений состоит в том, чтобы позволить вашему представлению создать этот экземпляр (да, локатор модели представления = представление сначала):
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 Locator - это оболочка вокруг некоторого (любого) контейнера Inversion of Control, такого как, например, Unity.
Ссылаться на:
Пример реализации ответа @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}}">