We thought of an alternative approach which I believe might be a more elegant solution. The approach consist of obtaining the region manager returned by the call of the
Region.Add method, through the NavigationResult
passed in the navigation callback from the RequestNavigate method instead of using a shared dictionary to save it.
As the result you will be able to navigate with scoped regions, like in the following code snippet:
new Uri("HelloWorldView?createRegionManagerScope=true", UriKind.Relative),
var myRegionManager = result.ExtractRegionManager();
myRegionManager.RequestNavigate("NestedRegion", new Uri("View1", UriKind.Relative));
Below you will find the details of this implementation:
To achieve this scenario, the first thing we needed was a NavigationResult that allows us to pass the region manager instance. For this we created a
CustomNavigationResult class that inherits from NavigationResult, but with another constructor which added an
IRegionManager parameter, that sets its RegionManager property.
Next, to obtain the region manager returned by the call of the Region.Add method, we needed the
LoadContent method in the RegionNavigationContentLoader to return a
Tuple<object,IRegionManager> instead of only an
object view. Therefore we created a CustomRegionNavigationContentLoader and its corresponding interface. Also we changed the name of the parameter passed with the view when navigating to "createRegionManagerScope"
and verify if its value is “true” to specify if a new region manager must be created (in case scoped regions are used).
This changes can be seen in the following code snippet:
public Tuple<object, IRegionManager> LoadContent(IRegion region, NavigationContext navigationContext)
if (view != null)
return new Tuple<object, IRegionManager>(view, region.RegionManager);
view = this.CreateNewRegionItem(candidateTargetContract);
bool createRegionManagerScope = navigationContext.Parameters["createRegionManagerScope"] == "true";
var rm = region.Add(view, null, createRegionManagerScope);
return new Tuple<object, IRegionManager>(view, rm);
Also, as the ExecuteNavigation method in the RegionNavigationService
is the one that calls the LoadContent method, we had to create a
CustomRegionNavigationService, to let it receive the returned tuple. In this same method instead of passing the default
NavigationResult to the navigationCallback we pass our
CustomNavigationResult with the returned region manager as a parameter.
You can find this modifications in the following code snippet:
private void ExecuteNavigation(NavigationContext navigationContext, object activeViews, Action<NavigationResult> navigationCallback)
Tuple<object, IRegionManager> tuple = this.regionNavigationContentLoader.LoadContent(this.Region, navigationContext);
// Raise the navigating event just before activing the view.
// Update the navigation journal before notifying others of navigaton
IRegionNavigationJournalEntry journalEntry = this.serviceLocator.GetInstance<IRegionNavigationJournalEntry>();
journalEntry.Uri = navigationContext.Uri;
// The view can be informed of navigation
InvokeOnNavigationAwareElement(tuple.Item1, (n) => n.OnNavigatedTo(navigationContext));
navigationCallback(new CustomNavigationResult(navigationContext, true, tuple.Item2));
Finally to avoid modifying the prism library we used the export attribute at the top of our custom classes (CustomRegionNavigationContentLoader
and CustomRegionNavigationService) which allows the
MefBootstrapper to provide these classes as a default implementation.
How to use:
If you apply this changes, then you will be able to call the RequestNavigate
method with a delegate method as the navigation callback, which will receive a
NavigationResult. The only problem is that you will have to cast the
NavigationResult to our CustomNavigationResult
class, in order to obtain the desired regionManager. Hence we created an extension method called
ExtractRegionManager in the NavigationResultExtension
class, which will make things easier.
For those interested, we prepared a sample application that portrays the aforementioned modifications. This sample shows two instances of a view being navigated to using the
RequestNavigate method, inside a region in a TabControl. Each of these views has an inner region, and because this region names will be duplicated they must be defined as scoped regions. You can find the application sample
Skydrive account under the name NavigationWithScopedRegionSample.
I hope you find this useful