Хто повинен контролювати навігацію в додатку MVVM?


33

Приклад №1: У моєму додатку MVVM відображається подання (давайте використовувати Silverlight для обговорення) і натискаю кнопку, яка повинна перенести мене на нову сторінку.

Приклад №2: У цьому ж представленні є ще одна кнопка, яка при натисканні повинна відкрити перегляд деталей у дочірньому вікні (діалоговому вікні).

Ми знаємо, що знайдуться об’єкти Command, відкриті нашим ViewModel, прив’язаними до кнопок методами, які реагують на натискання користувача. Але, що ж тоді? Як ми закінчимо дію? Навіть якщо ми використовуємо так звану Навігаційну службу, що це ми говоримо?

Якщо бути більш конкретним, в традиційній моделі перегляду для першого перегляду (наприклад, навігаційних схемах на основі URL, таких як в Інтернеті або вбудованій в SL навігаційну рамку) об'єкти Command повинні знати, який вид відображати далі. Здається, це переходить лінію, коли мова йде про відокремлення проблем, що сприяють цій схемі.

З іншого боку, якщо кнопка не була підключена до об'єкта Command і вела себе як гіперпосилання, правила навігації можна було б визначити у розмітці. Але чи хочемо ми, щоб Views контролював потік додатків, а чи не навігація - це лише інший тип бізнес-логіки? (Я можу сказати, що так у деяких випадках, а в інших - ні.)

На мій погляд, утопічна реалізація шаблону MVVM (і я чув, як інші це сповідують) полягала б у тому, щоб ViewModel був провідним таким чином, що додаток може працювати без голови (тобто немає переглядів). Це забезпечує найбільшу площу поверхні для тестування на основі коду і робить Погляди справжньою поверхнею програми. І моєму ViewModel не повинно бути байдуже, чи відображається воно в головному вікні, плаваючій панелі чи дочірньому вікні, чи не так?

Згідно з цією програмою, за деяким іншим механізмом під час виконання потрібно "прив'язувати" те, що перегляд повинен відображатися для кожного ViewModel. Але що робити, якщо ми хочемо поділитись Переглядом з кількома ViewModels або навпаки?

Отже, враховуючи необхідність керувати відносинами View-ViewModel, щоб ми знали, що потрібно відображати, коли поряд з необхідністю переходити між переглядами, включаючи відображення дочірніх вікон / діалогів, як ми справді досягаємо цього в шаблоні MVVM?

Відповіді:


21

Навігація завжди повинна керуватися у ViewModel.

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

У мене зазвичай є ApplicationViewModelабо ShellViewModel, що обробляє загальний стан моєї заявки. Сюди входить CurrentPage(що є ViewModel) та код для обробки ChangePageEvents. (Він також часто використовується для інших об'єктів, що застосовуються в додатку, таких як CurrentUser або ErrorMessages також)

Отже, якщо будь-який ViewModel, де завгодно, транслює a ChangePageEvent(new SomePageViewModel), ShellViewModelзабирає це повідомлення і переключається на ту CurrentPageсторінку, яку було вказано в повідомленні.

Я фактично написав повідомлення в блозі про навігацію з MVVM, якщо вам цікаво


2
Цікавий підхід. Чотири коментарі: 1) Silverlight не підтримує властивість DataType на DataTemplate, тому відображення DataTemplate в ViewModel неможливо в SL. 2) Це не стосується багатьох можливостей між переглядами та ViewModels. 3) Він не обробляє дочірні вікна (або, принаймні, не бачу як). 4) Це вимагає жорсткого з'єднання між Вашим Application / Shell ViewModel та його дітьми (онуками тощо). Якщо у мене в додатку 40 сторінок, цей ViewModel міг би бути громіздким для управління.
SonOfPirate

Але це, безумовно, щось, що слід врахувати.
SonOfPirate

@SonOfPirate 1) Silverlight не підтримує неявне відображення DataTemplate (поки що), проте він підтримує a DataTemplateSelector, що я зазвичай використовую для програм Silverlight. 2) Я раніше це використовував у багатьох ситуаціях. Наприклад, один ViewModel може мати кілька представлень даних, або один View може бути пов'язаний з декількома ViewModels. Як приклад першого, см rachel53461.wordpress.com/2011/05/28 / ... . Надалі вам просто потрібно вказати, що кілька ViewModels відображатимуть один і той же вигляд у вашому DataTemplateSelector.
Рейчел

3) Це лише основний приклад. Якби ви знали, що у вашій програмі буде кілька вікон, ви, очевидно, змінили ShellViewModel для обробки декількох CurrentPages4) Ще раз, це був лише основний приклад. Насправді всі мої PageViewModels базуються на якомусь базовому класі, тому моя ShellViewModel працює лише з базовим класом або інтерфейсом, наприклад IPageViewModel. Дійсно найбільший безладний фрагмент відображення - це DataTemplateSelector, який мав би зіставити 40 переглядів 40 ViewModels.
Рейчел

@SonOfPirate Я сподіваюся, що відповів на деякі ваші запитання. Не соромтеся шукати мене в чаті, якщо у вас є інші :)
Рейчел

6

Для закриття я думав, що опублікую напрямок, який я нарешті вибрав для вирішення цієї проблеми.

Першим рішенням було використовувати рамки навігації Silverlight на сторінці, що надаються поза коробкою. Це рішення базувалося на кількох чинниках, включаючи знання про те, що цей тип навігації передається Майкрософт у додатки метро Windows 8 і відповідає навігації в додатках Phone 7.

Для того, щоб це працювало, я далі розглянув роботу, яку ASP.NET MVC провів за допомогою звичайної навігації. Елемент управління Frame використовує URI, щоб знайти "сторінку" для відображення. Подібність дала можливість використовувати аналогічний підхід на основі конвенцій у додатку Silverlight. Трюк змусив це все працювати разом у форматі MVVM.

Рішення - це NavigationService. Ця служба пропонує декілька методів, таких як NavigateTo та Back, які ViewModels можуть використовувати для ініціювання зміни сторінки. Коли запитується нова сторінка, NavigationService надсилає CurrentPageChangedMessage за допомогою функції MVVMLight Messenger.

У поданні, яке містить елемент управління Frame, є власний набір ViewModel як DataContext, який слухає це повідомлення. Після отримання ім'я нового представлення вводиться за допомогою функції відображення, яка застосовує наші правила конвенції та встановлюється до властивості CurrentPage. Властивість Source елемента управління Frame прив'язана до властивості CurrentPage. Як результат, налаштування властивості оновлює Джерело та запускає навігацію.

Повернення до служби навігації. Метод NavigateTo приймає назву цільової сторінки. Щоб переконатися, що у ViewModels немає проблем із користувальницьким інтерфейсом, використовуване ім'я - це ім'я ViewModel для відображення. Я фактично створив перерахування, яке містить поле для кожного навігаційного ViewModel як помічника та для усунення магічних рядків у всьому додатку. Функція відображення, яку я згадав вище, зніме суфікс "ViewModel" з імені, додасть "Сторінку" до імені та встановить повне ім'я на "Перегляди {Ім'я} Сторінка.xaml".

Так, наприклад, щоб перейти до перегляду деталей про клієнта, я можу зателефонувати:

NavigationService.NavigateTo(ViewModels.CustomerDetails);

Значення CustomerDetails - "CustomerDetailsViewModel", яке відображається на "Views \ CustomerDetailsPage.xaml".

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

Сподіваюся, що пояснення допомагає.


2

Подібно до того, що сказала Рейчел, у мого додатку MVVM в основному є Presenterперемикач між вікнами або сторінками. Рейчел називає це ApplicationViewModel, але, на мій досвід, це, як правило, більше, ніж просто обов'язкова ціль (наприклад, отримання повідомлень, створення Windows тощо), тому технічно це більше схоже на традиційне Presenterабо Controller.

У моїй заяві моє Presenterпочинається з а CurrentViewModel. PresenterПерехоплює всі комунікації між Viewі ViewModel. Однією з речей, які ViewModelможна зробити під час взаємодії, є повернення нової ViewModel, а це означає, що нова сторінка чи нова Windowповинна бути відображена. PresenterПіклується про створення або перезапису Viewдля нового ViewModelі встановивши DataContext.

Результатом дії також може бути те, що a ViewModelє "завершеним", і в цьому випадку він Presenterвиявляє це і закриває вікно, або вискакує це ViewModelз стека VM і повертається до відображення попередньої сторінки.


Як ведучий знає, який вид відображати?
SonOfPirate

1
@SonOfPirate - це зазвичай робиться через механізми WPF. В Presenterтільки палички возвращаемом ViewModelв візуальному дереві, і WPF захоплює відповідний View, перехоплює вгору DataContextі пута , що в візуальному дереві замість цього. Це можна зробити, використовуючи DataTemplates, які оголошують, який ViewModelтип вони надають, або ви можете створити спеціальний селектор шаблонів даних. Це все ще в царині функцій WPF.
Скотт Вітлок
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.