Navigation and ScopedRegions

Topics: Prism v4 - WPF 4
Dec 2, 2010 at 12:05 PM

I'm trying to swich my WPF4 app over to using the Navigation methods but am running into some potential road blocks.  I have a Tab based application, with the same view being opened multiple times in the Tab and that view having a region inside it.  Basic views - CustomerListView as the initial tab, click on Customer name to open CustomerEditView as other tab(s).  Customer edit has a contentcontrol as a Region that hosts many different CustomerDetailViews.  Currently I am using scoped regions to accomplish this.  In trying to switch to the Navigation methods I don't know how to get the RequestNavigate to create a ScopedRegion that my CustomerEditView can use.  I need the ScopedRegionManager in my CustomerEditViewModel since the detail views are loaded on demand and therefor injected (as opposed to discovered).  I could have 10-12 different detail views and don't think I want to load them all up at the initial opening of the CustomerEditView.

Can I do this with Navigation?  Are there any other suggestions?  One thought I had was rather than using a scoped region in my CustomerEditView I could use a content control that points to a property in my CustomerEditViewModel that has the currently selected DetailViewModel and use DataTemplates to re-load the DetailView each time the DetailViewModel changes but that seems like a lot of overhead.

Any thoughts or suggestions are appreciated!!

Developer
Dec 2, 2010 at 4:31 PM

Hi,

Prism Region Navigation does not provide a way of using Scoped Regions out-of-the-box. However, you might use an alternative approach to achieve the scoped regions scenario while keeping the benefits of navigation.

When you use Scoped Regions, it means that you have multiple RegionManager instances across your views. When you add a view to a region through the View Injection approach specifying that you want to create a scoped RegionManager, your region internally calls the RegionManager.SetRegionManager method adding a new instance of an object that implements IRegionManager (generally a RegionManager).

In this case, since you're not using the View Injection approach, you could call the RegionManager.SetRegionManager method by yourself, as in the following code:

RegionManager.SetRegionManager(viewInstance, new RegionManager());

for example in your view's constructor, or in a method subscribed to the Region's ViewCollection CollectionChanged event.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

 

Dec 12, 2010 at 4:36 PM
Edited Dec 14, 2010 at 10:38 AM

Hi hnolan and Guido,

I'm facing exactly the same case as descrobed by hnolan.

I tried adding this at the view's c'tor:

	public JobPlanningView(JobPlanningViewModel viewModel)
	{
		m_scopedRegionsManager = new RegionManager();
		RegionManager.SetRegionManager(this, m_scopedRegionsManager);
		InitializeComponent();

		// Set the ViewModel as this View's data context.
		this.DataContext = viewModel;
	} 

But still no good - same exception by RegionManager.
Do you have any further insights?...
Thanks,
Ziev
Dec 13, 2010 at 4:33 PM

I am having the same problem. I need use scoped regions in many views so it would be interesting to know what to do in this scenario (Region Navigation).

Thanks in advance.

Anti

Developer
Dec 13, 2010 at 5:47 PM
Edited Apr 8, 2011 at 1:57 PM

Hi Ziev and Anti,

When you navigate to a view inside a region, the RegionNavigationService internally calls the LoadContent method of the RegionNavigationContentLoader it has associated. As explained on this article by Karl Shifflett, there are two possibilities:

A) If there is an active view in the region that can handle the navigation request, the LoadContent method will return that view, and

B) If there isn't, the LoadContent method will create the view and add it to the region.

This can be seen in the following code (which is a fragment of the LoadContent method inside the default RegionNavigationContentLoader:

 

public object LoadContent(IRegion region, NavigationContext navigationContext)

{

(...)

var view = acceptingCandidates.FirstOrDefault();

if (view != null)
{
return view;
}

view = this.CreateNewRegionItem(candidateTargetContract);

region.Add(view);

return view;

}

In case you want to use Scoped Regions with Navigation and you want the view to be created and added to the region by the RequestNavigate method (that is, case B), you will need to create a new RegionNavigationContentLoader that adds the views using a Scoped Region Manager. The LoadContent method of that custom RegionNavigationContentLoader could look like this:

public object LoadContent(IRegion region, NavigationContext navigationContext)

{

(...)


var view = acceptingCandidates.FirstOrDefault();

if (view != null)
{
return view;
}

view = this.CreateNewRegionItem(candidateTargetContract);
region.Add(view, null, true);

return view;
}

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Dec 14, 2010 at 10:40 AM

Thank you Guido, for the detailed answer.

It does make sense now. If I'll decide to implement it in my system I will post feedback here.

Regards,
Ziev

Dec 15, 2010 at 11:50 AM

Thank you too, Guido.

I have realized that my application was a little bit confusing due to the use of scoped regions. I think is better to use the whole power of the Region Navigation API and to activate - deactivate views instead of clearing the region and reconstructing the views (that was the reason why i decided to use scoped regions).

Perhaps, I am implementing this mechanism in the future, so as Ziev have said, I will let you know.

Thanks again

Anti

Jan 31, 2011 at 8:53 AM

I ran into the same scenario where I needed to use the scoped region. This is how I implemented in my solution:

The application can create multiple instances of RecordView. Here the xaml of the RecordView

...
<ContentControl Prism:RegionManager.RegionManager="{Binding Path=ScopedRegionManager}" Prism:RegionManager.RegionName="RecordMenuRegion" />
<ContentControl Prism:RegionManager.RegionManager="{Binding Path=ScopedRegionManager}" Prism:RegionManager.RegionName="RecordMainRegion" />
...

In RecordViewModel

private IRegionManager scopedRegionManager;
[ImportingConstructor]
public RecordViewModel(IRegionManager regionManager, ....)
{
 ...
 this.scopedRegionManager = regionManager.CreateRegionManager();
 //then register this scopedRegionManager with private Dictionary Collection to be used later
 //in my case I created an IScopedRegionManagerCollection to track registrations with Add and Remove methods
 //whenever the instance of this RecordView is destroy, remove scoped region manager from the collection
}

public IRegionManager ScopedRegionManager
{
 get{return this.scopedRegionManager;}
}

I hope this makes some sense.

 

Mar 11, 2011 at 3:29 AM

Hmm, I havent been looking at the discussion board for some time, I needed scoped regions with navigation API back in December. It wasnt an elegant solution but I added the the scoped region manager functionality by forking Prism code.

 

1. Added IRegionManager property to NavigationContext class (this was to let every view know about its region manager as Navigation context is passed into OnNavigatedTo(..) )

2. Modified LoadContent method of RegionNavigationContentLoader

public class RegionNavigationContentLoader : IRegionNavigationContentLoader
{

...

 public object LoadContent(IRegion region, NavigationContext navigationContext, bool createScopedRegionManager)
 {

...

navigationContext.RegionManager = region.Add(view, view.GetHashCode().ToString(), createScopedRegionManager);

return view;

 }

...

}

3. Added an overloaded method to RegionManagerExtensions to include the bool for regionScope

 

public static void RequestNavigate(this IRegionManager regionManager, string regionName, Uri source, Action<NavigationResult> navigationCallback, bool createScopedRegion)
{

...

}

 

But now I see some interesting suggestions in this post, will see if I could use these "external" ways so I can stick to Prism codebase. Anyone had any luck?

Apr 1, 2012 at 5:50 PM

I came up with simpler solution for the scope problem. In short I am using custom attribute to determine if scope is required when view is created and using RegionManager.RegionManager attached attribute to access correct instance of RegionManager within the view.
More information is available at this link:
http://www.codeproject.com/Articles/320673/PrismScopedRegions