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

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

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

3 ответов


интро

в MVVM обычная практика состоит в том, чтобы взгляды находили свои ViewModels, разрешая их из инъекции зависимостей (DI) контейнер. Это происходит автоматически, когда контейнеру предлагается предоставить (разрешить) экземпляр класса представления. Контейнер удалить ViewModel в представление, вызывая конструктор представления, который принимает параметр ViewModel; эта схема называется инверсия управления (МОК.)

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

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

проблемы, вытекающие из DI

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

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

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

Enter ViewModelLocator

в ViewModelLocator дополнительная абстрактность такой:

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

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

резюме

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

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

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


пример реализации @Джон

у меня есть класс модели представления локатор. Каждое свойство будет экземпляром модели представления, которую я собираюсь выделить в своем представлении. Я могу проверить, работает ли код в режиме разработки или не использует 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) для вашей viewmodel:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">

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

цель локатора модели представления-позволить вашему представлению создать экземпляр этого (да, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

вместо этого:

public void MyWindowViewModel()
{
}

объявив этот:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

здесь ViewModelLocator это класс, который ссылается на IoC, и именно так он решает MainWindowModel собственность это разоблачает.

это не имеет ничего общего с предоставлением моделей Mock view для вашего представления. Если вы хотите этого, просто сделайте

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

локатор модели представления-это оболочка вокруг некоторой (любой) инверсии контейнера управления, например Unity.

см.: