Что такое 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}}">
Другие вопросы по тегам