PRISM WPF — Навигация каждый раз создает новый вид

#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());