#wpf #navigation #unity-container #prism
#wpf #навигация #unity-контейнер #prism
Вопрос:
Я использую PRISM 4 Navigation API с Unity в WPF. У меня есть древовидное представление, которое инициирует RequestNavigate, передающий идентификатор выбранного узла дерева (GUID).
_regionManager.RequestNavigate(RegionNames.DetailRegion,
ViewNames.SiteView "?ID=" site.ID);
В моем модуле я зарегистрировал представление / view-model следующим образом:
_container.RegisterType<SiteDetailsViewModel>();
_container.RegisterType<object, SiteDetailsView>(ViewNames.SiteView);
Когда я выбираю разные узлы из древовидного представления, DetailsRegion отображает SiteDetailsView, как и ожидалось, но когда мне хочется вернуться к тому же узлу, создается новый вид / view-model.
Я пытался прерваться на IsNavigationTarget(NavigationContext navigationContext)
, но этот метод, похоже, никогда не вызывается.
Где я ошибся? Заранее спасибо.
Ответ №1:
Проблема была в таком месте, которого я никогда не ожидал… Отладка API навигации привела меня к RegionNavigationContentLoader
public object LoadContent(IRegion region, NavigationContext navigationContext)
Когда я продвинулся дальше по коду, я заметил вызов:
protected virtual IEnumerable<object> GetCandidatesFromRegion(
IRegion region,
string candidateNavigationContract)
Я заметил, что именование здесь является ключом к сопоставлению представления с view-model.
В моем примере название для каждой части было:
public class SiteDetailsViewModel { ... } // ViewModel
public class SiteDetailsView { ... } // View
ViewNames.SiteView = "SiteView" // ViewName constant
Когда я непреднамеренно внес следующее изменение:
ViewName.SiteView = "SiteDetailsView"
Все сработало.
Заключение
Имя ViewModel должно начинаться с того же имени, которое вы использовали для идентификации вашего представления.
Я протестировал это, изменив свой вид на:
public class MyView { ... }
и по-прежнему использует одно и то же имя представления для регистрации в контейнере и навигации:
_container.RegisterType<object, MyView>(ViewNames.SiteView);
...
_regionManager.RequestNavigate(RegionNames.DetailRegion,
ViewNames.SiteView "?ID=" site.ID);
Похоже, это тоже работает. Таким образом, кажется, что имя View-Model неразрывно связано с именем view, используемым для перехода к этому представлению.
ПРИМЕЧАНИЕ
Это только при использовании IoC и Unity с навигационным API PRISM 4. Похоже, этого не происходит при использовании MEF.
Дальнейшее исследование
Я также знаю, что некоторые руководства советовали нам использовать typeof(MyView).FullName
при регистрации представления в контейнере…
_container.RegisterType<object, MyView>(typeof(MyView).FullName);
Лично я считаю, что это ошибка. Используя полное имя представления, вы создаете зависимость между представлением и любым пользователем, который желает перейти к этому представлению…
_regionManager.RequestNavigate(RegionNames.DetailRegion,
typeof(MyView).FullName "?ID=" site.ID);
Комментарии:
1. да, странное поведение, я нашел эту информацию в комментариях к этой проблеме compositewpf.codeplex.com/workitem/7468?PendingVoteId=7468
Ответ №2:
Проблема заключается в регистрации представления и ViewModel. Чтобы иметь только одно представление, вы должны использовать другой менеджер времени жизни. Без указания менеджера времени жизни используется TransientLifetimeManager
, который всегда возвращает новый экземпляр при разрешении. Чтобы иметь только один экземпляр, вы должны использовать ContainerControlledLifetimeManager
или HierarchicalLifetimeManager
:
_container.RegisterType<SiteDetailsViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<object, SiteDetailsView>(ViewNames.SiteView, new ContainerControlledLifetimeManager());