Навигация в сабжевых платформах проста, но доставляет некоторый зуд в некоторых местах при попытке красиво связать ее с MVVM. К примеру, в Windows Phone навигация происходит только по ccылкам с возможностью задать параметры только в URI (т.е. строками) что особенно доставляет удовольствие при перемещении представлений в другие папки
Navigate(Uri uri)
тогда как в Windows Store интерфейс немного улучшили и дали возможность передавать объект
Navigate(Type pageType, object argument)
Тут есть интересная особенность – argument должен быть сериализуемым, иначе он пройдет на страницу успешно но поломает NavigationState (функция GetNavigationState зависнет при вызове).
Собственно, задача mvvm-навигации – это красивое связывание представлений с вьюмоделями и передача вьюмоделей при переходе на представления. Задавшись такой задачей я написал небольшой тулкит с примерами для Windows Store и Windows Phone.
Как это выглядит?
Регистрация
И так, сперва нам надо собственно связать представления с вьюмоделями (в качестве IoC я использую Autofac в примерах):
_navigationBuilder
.RegisterViewModel<FrameViewModel>().StaticResource().WithoutView()
.RegisterViewModel<MainViewModel>().Singleton().ForView<MainPage>()
.RegisterViewModel<NotesViewModel>().StaticResource().ForView<NotesView>()
.RegisterViewModel<NoteViewModel>().ForView<NoteView>()
.RegisterViewModel<AppSettingsViewModel>().ForView<AppSettingsView>()
;
Как можно заметить, регистрация имеет fluent-стиль. StaticResource(), к примеру, означает, что вьюмодель будет доступна из XAML как StaticResource по имени как ключу. Т.е. в любом месте вы сможете написать что-нибудь типа
{Binding Notes.Count, Source={StaticResouce NotesViewModel}}
Singleton(), как и понятно из названия, зарегистрирует вьюмодель как синглитон в контейнере. ForView же выполнит главную задачу – сохранит маппинг вьюмодели с представлением. Использование тулкита потребует от вас реализовать интерфейс IIoCContainerBuilder (я решил не завязывать его на конкретные DI реализации типа Autofac), а так же отнаследовать все страницы от класса NavigatablePage.
Как открыть новую страницу из вьюмодели?
Внтри вьюмодели вы естественно ничего не знаете о представлении и поэтому переход осуществляется по средствам вызова метода Show (или ShowCommand) у целевой вьюмодели, который при помощи скрытой магии откроет страницу и передаст ей вьюмодель как DataContext. Т.е. можете в исходной вьюмодели хранить ссылку на целевую (через constructorproperty injection) либо напрямую через ServiceLocator
ServiceLocator.Resolve<TargetViewModel>().Show();
Как работает передача вьюмодели на страницу?
Как я уже говорил, API не предлагает нормальных инструментов передачи сложных объектов как параметров к страницам, поэтому я обошёл проблему созданием специального реестра вьюмоделей (словарь пар Guid-ViewModel) и передачей идентификатора вьюмодели из реестра как строки в параметры к странице. Далее страница на OnNavigateTo достает вьюмодель по переданному ключу. Cделать это можно было и через MVVM Light Messanger, но я решил не добавлять лишние библиотеки в зависимости к тулкиту.
Заключение
Я описал малую часть тулкита и примеров, поэтому, советую с ними ознакомиться и надеюсь, что вы найдете некоторые интересные решения. По желанию, могу оформить это как шаблон проекта для Visual Studio.
git: github.com/EgorBo/MvvmNavigationToolkit
Автор: Nagg