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

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

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

Ответы [ 3 ]

188 голосов
/ 28 марта 2011

Введение

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

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

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

Проблемы, связанные с DI

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

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

Однако, во время разработки нет нашего кода, работающего . Дизайнер пытается использовать отражение для создания экземпляров наших представлений, что означает, что:

  • Если для конструктора View требуется экземпляр ViewModel, то дизайнер вообще не сможет создать экземпляр View - он каким-то контролируемым образом выдаст ошибку
  • Если представление имеет конструктор без параметров, представление будет создано, но его DataContext будет null, поэтому мы получим «пустое» представление в конструкторе, что не очень полезно

Введите ViewModelLocator

ViewModelLocator - это дополнительная абстракция, используемая следующим образом:

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

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

Резюме

ViewModelLocator - это идиома, которая позволяет вам сохранить преимущества DI в приложении MVVM, а также позволяет вашему коду хорошо работать с визуальными дизайнерами. Иногда это называется «смешиваемостью» вашего приложения (имеется в виду Expression Blend).

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

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

8 голосов
/ 01 марта 2015

Пример реализации @ ответа Джона

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

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

Цель локатора модели представлений - позволить вашему представлению создать это экземпляр (да, локатор модели вида = сначала посмотреть):

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}"

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

См .:

...