PRISM WPF - Navigation creates new view every time
The problem was in such a place that I never expected... Debugging the Navigation API lead me to the RegionNavigationContentLoader
public object LoadContent(IRegion region, NavigationContext navigationContext)
When i stepped further down the code, I noticed a call to:
protected virtual IEnumerable<object> GetCandidatesFromRegion( IRegion region, string candidateNavigationContract)
I noticed that the naming here is key to matching the view to the view-model.
In my example, the name for each part was:
public class SiteDetailsViewModel { ... } // ViewModelpublic class SiteDetailsView { ... } // ViewViewNames.SiteView = "SiteView" // ViewName constant
When I inadvertently made the following change:
ViewName.SiteView = "SiteDetailsView"
Everthing worked.
Conclusion
The name of the ViewModel must start with the same name you used to identify your view.
I tested this out by changing my view to:
public class MyView { ... }
and still using the same view name to register with the container and navigation:
_container.RegisterType<object, MyView>(ViewNames.SiteView);..._regionManager.RequestNavigate(RegionNames.DetailRegion, ViewNames.SiteView + "?ID=" + site.ID);
This seems to work also. So it seems the name of the View-Model is intrinsically linked to the view name used to navigate to that view.
NOTE
This is only when you're using IoC and Unity with the PRISM 4 Navigation API. This doesn't seem to happen when using MEF.
Further Investigation
I am also aware that some guides have told us to use the typeof(MyView).FullName
when registering the view with the Container...
_container.RegisterType<object, MyView>(typeof(MyView).FullName);
I personally think this is a mistake. By using the view's full name, you are creating a depending between the view and any one who wishes to navigate to that view...
_regionManager.RequestNavigate(RegionNames.DetailRegion, typeof(MyView).FullName + "?ID=" + site.ID);
The registration of the View and the ViewModel is the problem. To have only one view you have to use a different lifetime manager. Without specifying a lifetime manager the TransientLifetimeManager
is used which always returns a new instance on resolve. To have only one single instance you have to use the ContainerControlledLifetimeManager
or the HierarchicalLifetimeManager
:
_container.RegisterType<SiteDetailsViewModel>(new ContainerControlledLifetimeManager());_container.RegisterType<object, SiteDetailsView>(ViewNames.SiteView, new ContainerControlledLifetimeManager());