Вступ
У 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, оскільки ви можете використовувати шаблон даних замість цього.