Introduction

Region in Prism is a named area of the screen where content may be rendered. (More information about regions and UI composition is available here).

Regions designed to contain any number of views of any type. If region contains just a single view everything is simple. Complications start when multiple views are associated with the same region. When views are of the same type and contain nested regions conflict occur. RegionManager requires all regions to have unique name but when more then one nested region is created the name is no longer unique.

To work around this issue RegionManager may create a scope and put view in that scope. Essentially a new instance
of RegionManager is created and assigned to the view. This isolates the view from other views within parent region. More information about Scoped RegionManager available
here.

Problem

Implementation of Scoped Regions support could be divided into two separate aspects:

At the time of creating the View PRISM library needs to be told (perhaps dynamically, on case by case bases) that Scoped Region is required for this particular View.

Once View is created it should have direct access to the RegionManager associated with the view and its scope.

Solution

Require scoped region

During View Discovery or Region Navigation Prism implementation simply resolves View instance from DI container and adds it to the region. To enable support for scoped regions we need to inject code in between these two steps to determine if Scope is required and if yes add view to the region with the scope. We must have some kind of flag indicating if Scoped Region is required. This information could be delivered in few different ways:

A View class may implement certain Interface to indicate that scope is required. There could be a member on it returning Boolean indicating if scope is required at runtime.

Attribute could be used to mark the type as required scoped region.

There are other ways but for simplicity we will use these two.

Access correct region manager

Solution to second problem is not so straight forward. There is no direct access to an instance of RegionManager created for View’s scope. No reference is returned and no list of nested scope region managers is available anywhere in the region tree. To solve this obstacle we could use RegionManager.RegionManager attached property. When the View is being added to a Region it initializes this attached property with reference to an instance of RegionManager that handles this particular region. If Scoped region has been requested it would hold a reference to an instance of scopedRegionManager instead of parent/root RegionManager. To gain access to the correct instance of RegionManager all we have to do is to bind this attached property to a member parameter of the view. After all attached properties were designed just for that.

It creates new view, adds it to the region and returns new view to caller. So to extend this behavior and add support for scoped regions all we have to do is to determine if scoped region is required and add view with the scope. The most logical way would be to pass a flag as a parameter to LoadContent function. Unfortunately during view discovery or region navigation content loader is called internally by the PRISM library and there is no easy way to change it. Instead of redesigning PRISM we could make View class to carry this information. To do so we could mark type with custom attribute or make class to implement certain interface. There are pros and cons for each of these methods so for demonstration purposes I’ll implement both.

Interface

In order to add a view to a region with a scope IRegion.Add(view, viewName, createRegionManagerScope) method should be called. In this function the view parameter is the new view we just created and the Name and Scope flag are missing info we need to provide. We will implement interface that deliver missing pieces of information:

To unify access to both the interface and the attribute I’ve made the attribute to implement IProvideRegionScopeInfo interface as well. So after adding all the necessary checks to the code we should have something like this:

Accessing RegionManager

Gaining access to an appropriate region manager requires few extra lines of code. Usually we would use dependency injection container to resolve
IRegionManager interface. We can still do that to access
root RegionManager but in order to access
RegionManager responsible for this instance of the View we have to use RegionManager.RegionManager
attached property. Doing so is very simple and is done in View’s constructor with following code:

Mode of the binding is BindingMode.OneWayToSource because we do not want to allow this property to be changeable from within the source. Source is either a View or ViewModel or both if separate Binding object is used.

So, combination of these two enhancements would allow using Scoped Regions with View discovery as well as with PRISM region navigation.

Using the code

I’ve included sample application demonstrating this solution. In order to use this code in your projects just add RegionNavigationScopedContentLoader.cs to your solution and register it with DI container instead of default implementation. For sample please reference
Bootstrapper.cs file.

Credits

Sample for this article is based on the project posted on Prism Work Item 8927. It was modified to implement this fix but preserved rest of the project to enable comparison.

Thanks for the article. I followed the article and it works fine. I tried to apply this to a slightly different scenario. I have a list of customers in a list view on the left region of the shell, and Detail region on the right.

Each customer Item has an edit button. When you click the edit customer button it will show a tab Edit view in Details region on the right. When we click to edit another customer it will add as a new tab.This Details region also has child views to contacts and Addresses.I followed your project and it worked for the first tab, but when I try to edit the second customer it gives the error:

“An exception occurred while creating a region with name 'NestedRegion'. The exception was: System.ArgumentException: Region with the given name is already registered: NestedRegion “

In your sample it add both “HelloWorldView” views on module initialisation using

If region has only one view it does not make any sense to create it in its own scope since it is relatively expensive in terms of resources. The operation and binding process are the same as for any regular region. Same goes for the nested regions. The only difference is in how Region Manager is being accessed. You need to use attached property to get the correct manager.Perhaps if you could provide more details on your issue I could be a better help.

Gaining access to an appropriate region manager requires few extra lines of code. If you would use dependency injection container to resolve IRegionManager interface you would get root manager. To access RegionManager responsible for local region use RegionManager.RegionManager attached property of the view. I have code in the article demonstrating this method.