Що таке ViewModelLocator та які його плюси / мінуси порівняно з DataTemplates?


112

Чи може хтось дати мені короткий підсумок того, що таке ViewModelLocator, як він працює, і які плюси / мінуси для його використання порівняно з DataTemplates?

Я спробував знайти інформацію в Google, але, здається, існує багато різних її реалізацій і немає переліку складностей щодо того, що це таке, і плюси / мінуси її використання.

Відповіді:


204

Вступ

У MVVM звичайна практика полягає в тому, щоб представники знаходили свої ViewModels, вирішуючи їх з контейнера введення залежності (DI). Це відбувається автоматично, коли контейнеру пропонується надати (вирішити) екземпляр класу View. Контейнер вводить ViewModel у View, викликаючи конструктор View, який приймає параметр ViewModel; ця схема називається інверсією управління (IoC).

Переваги DI

Основна перевага тут полягає в тому, що контейнер може бути налаштований під час роботи з інструкціями щодо вирішення типів, які ми вимагаємо від нього. Це дозволяє досягти більшої доказовості, доручивши їй розв’язувати типи (Views та ViewModels), які ми використовуємо, коли наш додаток насправді працює, але вказуючи його по-іншому, коли виконуються одиничні тести програми. В останньому випадку додаток навіть не матиме інтерфейсу користувача (він не працює; просто тести), тому контейнер вирішить макети замість "нормальних" типів, які використовуються під час роботи програми.

Проблеми, що випливають із DI

Поки ми бачили, що підхід DI дозволяє легко перевіряти додаток, додаючи шар абстракції над створенням компонентів програми. У цьому підході є одна проблема: він не дуже добре поєднується з візуальними дизайнерами, такими як Microsoft Expression Blend.

Проблема полягає в тому, що як у звичайних запусках додатків, так і в тестових запуску одиниць, хтось повинен встановити контейнер з інструкціями щодо того, які типи потрібно вирішити; крім того, хтось повинен попросити контейнер розв'язати представлення даних, щоб ViewModels міг бути введений у них.

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

  • Якщо конструктору View потрібен екземпляр ViewModel, дизайнер взагалі не зможе інстанціювати представлення даних - воно виправиться помилкою деяким контрольованим чином
  • Якщо у перегляду є конструктор без параметрів, Вигляд буде створено миттєво, але це DataContextбуде nullтак, ми отримаємо "порожній" погляд у дизайнера - що не дуже корисно

Введіть ViewModelLocator

ViewModelLocator - це додаткова абстракція, яка використовується таким чином:

  • Сам представник створює ViewModelLocator як частину своїх ресурсів і даних, прив'язує свій DataContext до властивості ViewModel локатора
  • Локатор якимось чином виявляє, чи ми перебуваємо в режимі проектування
  • Якщо не в режимі проектування, локатор повертає ViewModel, який він вирішує з контейнера DI, як пояснено вище
  • Якщо в режимі проектування, локатор повертає фіксовану "манекен" ViewModel, використовуючи власну логіку (пам’ятайте: контейнера в проектний час немає!); Цей ViewModel зазвичай популяризований фіктивними даними

Звичайно, це означає, що Погляд повинен мати для початку конструктор без параметрів (інакше дизайнер не зможе його інстанціювати).

Підсумок

ViewModelLocator - це ідіома, яка дозволяє зберігати переваги DI у вашому додатку MVVM, а також дозволяє вашому коду добре грати з візуальними дизайнерами. Іноді це називається "сумісністю" вашої програми (мається на увазі Expression Blend).

Після перетравлення вищезазначеного див. Практичний приклад тут .

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


4
+1 для чудового пояснення. Чи можете ви додатково розширити перегляд та його ресурси? Під ресурсами ви маєте на увазі властивості Перегляду? Або? Чи є у вас посилання на конкретний приклад до цього шаблону?
Метро Смурф

@MetroSmurf: Ваше посилання знаходиться у розділі Підсумок.
Джон

1
Дякую. Чи існують обмеження щодо використання ViewModelLocator? У мене були певні занепокоєння з приводу того, що він посилається на статичний ресурс - чи можна ViewModels створювати динамічно під час виконання? А чи багато додаткового коду, пов'язаного з підключенням?
Рейчел

@Rachel: Це ж посилання в резюме повинно відповісти на ці запитання на практичних прикладах.
Джон

2
Надзвичайно хибна відповідь. Основна мета Локатора перегляду моделей - не надавати дизайнеру фіктивних даних. Ви можете легко зробити це, вказавши d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}". Мета Локатора полягає в тому, щоб реально включити DI у видах, тому що WPF так погано надає його. Приклад: у вас є головне вікно, яке відкриває деяке діалогове вікно. Щоб вирішити DI в діалоговому вікні звичайним способом, вам потрібно буде пропустити його як залежність від головного вікна! Цього уникнути за допомогою локатора перегляду.
hyankov

10

Приклад реалізації відповіді @ Джон

У мене є клас перегляду моделей перегляду. Кожне властивість буде екземпляром моделі перегляду, яку я збираюся виділити у своєму представленні. Я можу перевірити, чи працює код у режимі проектування чи не використовується 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}}">

Чи є різниця у використанні thisзамість dummy?
Себастьян Ксавері Wiśniowiecki

5

Я не розумію, чому інші відповіді на це питання обертаються навколо Дизайнера.

Мета Локатора перегляду моделей полягає в тому, щоб дозволити Вашому представленню проаналізувати це (так, Переглянути локатор моделі = Переглянути перший):

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

Локатор перегляду моделей - це обгортка навколо якоїсь (будь-якої) інверсії контейнера управління, наприклад Unity.

Відноситься до:


Локатор моделі перегляду не потребує контейнера, користувач вирішує, як вирішити модель перегляду через конфігурацію, і ви можете використовувати контейнер або просто створити тип самостійно. Таким чином, ви можете робити розташування моделі перегляду на основі конвенції, наприклад, замість того, щоб попередньо зареєструвати всі свої представлення та переглянути моделі в якомусь контейнері.
Кріс Бордеман

Ви маєте рацію, кажучи: " Я не розумію, чому інші відповіді [...] обертаються навколо Дизайнера " +1, але суть локатора полягає в тому, щоб усунути з подання будь-які знання про те, як виглядає модель перегляду створено, роблячи погляд незалежним від цього уявлення, залишаючи це локатору. Локатор зможе надати різні смаки моделі перегляду, можливо, деякі спеціальні, додані за допомогою плагінів, якими керує локатор (і конкретний для проектування). Перегляд буде чистим від будь-якого цього процесу пошуку правильної версії моделі перегляду, що справді добре для SoC.
хвилини
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.