Nested Regions

Topics: Prism v2 - WPF 4
Mar 15, 2011 at 12:57 PM

Can anyone tell me why this doesn't work?

<Window x:Class="PrismRegions.View.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.codeplex.com/CompositeWPF"
        Title="Main View" Height="300" Width="300">
    <DockPanel>
        <Menu DockPanel.Dock="Top" cal:RegionManager.RegionName="MainMenu">
            <MenuItem Header="_File">
                <MenuItem Header="E_xit" Command="{Binding ExitCommand}" />
            </MenuItem>
            <MenuItem Header="_Edit" cal:RegionManager.RegionName="EditMenu" />
        </Menu>
        <ContentControl />
    </DockPanel>
</Window>

Clearly prism doesn't like regions declared within regions.

Incase you've never tried it, if you try and refer tothe EditMenu region in code it doesn't exist.

So what is the recommended way of achieving nested regions?

In summary my goals are:

  • Module 1 might want to inject an item onto the Edit menu
  • Module 2 might want to inject a whole new menu onto the menu bar.

 

Many thanks,

 

Developer
Mar 15, 2011 at 3:06 PM
Edited Mar 15, 2011 at 4:11 PM

Hi,

I've reproduced the scenario you've mentioned and found that, if you try to add a region directly inside another region, the ItemsControlRegionAdapter will fail to attach the RegionManagerProperty to the child region, thus making the RegionManagerRegistrationBehavior fail to register it into the RegionManager.

This happens because, when you create a region in an ItemsControl that already had some children in the Items collection, the ItemsControlRegionAdapter will clear the Items property, add them as views to the newly generated IRegion, and then set the control's ItemsSource to the IRegion's Views property. However, at the moment this is done, the RegionManagerRegistrationBehavior will have not registered the parent region in the RegionManager, hence its RegionManager property will be null. Therefore, since the Region's InnerAdd method sets the RegionManager.RegionManager attached property into the View that gets added to a region, the view (which in your case is itself a region) won't have its RegionManager property set.

The recommended approach for having nested regions is to add a view containing the nested region into the main region. For example, if you had your Menu region like this:

<Menu DockPanel.Dock="Top" cal:RegionManager.RegionName="MainMenu">
            <MenuItem Header="_File">
                <MenuItem Header="E_xit" Command="{Binding ExitCommand}" />
            </MenuItem>
</Menu>

You could programatically add the MenuItem region (in another module, for example):

 

IRegionManager manager = ServiceLocator.Current.GetInstance<IRegionManager>();
var menuItem = new MenuItem() { Header = "EditMenu" };
menuItem.SetValue(RegionManager.RegionNameProperty,"EditMenu");
manager.Regions["MainMenu"].Add(menuItem);

 

That way, you would avoid the timing issue I've described.

I hope you find this helpful.

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

Jan 16, 2012 at 8:05 PM

I'm experiencing a similar issue where I have a region "ShellRegion" in the ShellView, and I want to have another region in one of its nested views, then use the region manager to RequestNavigate the nested view.

What I've tried is XAMLy attributing both the controls that are used for regions with the prism RegionName attached prop.

When I try to refer to the child region, I get an exception that it doesn't exist.

Just to make clear I want to have a "MainRegion" that is a descendant of "ShellRegion", then I want to RequestNavigate a view inside the child region ("MainRegion")

Jan 16, 2012 at 11:11 PM
Edited May 19, 2012 at 11:43 PM

Found answer:

The nested region is added later at runtime after the shell region was registered in the Bootstrapper, and thus has to be explicitly re-registered, I've added the following code to the View that contains the child region ("MainRegion"):

 

public MainView(IRegionManager regionManager, MainViewModel vm)
{
  InitializeComponent();
  DataContext = vm;
  RegionManager.SetRegionManager(mainRegionControl, regionManager);
  RegionManager.UpdateRegions();
}

Now everything works perfect.

Jun 6, 2013 at 6:46 PM
I am having similar issue.

I am using a TabControl - regionName is set as "TabRegion" (hence ItemsControlRegionAdapter comes in play) and I add a tabItemView (with nested regions in it) to my TabItem.

_mainRegionManager.Regions["TabRegion"].Add(tabItemView);

I observe that my tabItemView in tabitem has RegionManagerProperty set to null.

If I use scoped regions for each tabItem using the following code:
_scopedRegionManager=_mainRegionManager.Regions["TabRegion"].Add(tabItemView,null,true);

then I am able to see _scopedRegionManager in my tabItemView using the following:
IRegionManager _rm=RegionManager.GetRegionManager(tabItemView);

But any view defined inside the regions in tabItemView returns null for RegionManager property.

I tried the solution proposed by GuidoMaliandi and weitzhandler but to no avail.

Could you kindly tell me how to get the RegionManager property in views within nested regions (this seems a generic issue with nested regions and not the scoped regions)?
Please let me know if I could provide more information.

Thanks,
RDV
Developer
Jun 6, 2013 at 8:45 PM
Hi RDV,

Have you tried using the RegionManagerAwareBehavior shown in my blog post?
I believe it could be helpful to work around your problem. Basically, after registering the region behavior in the Bootstrapper, any view or view model that implements IRegionManagerAware will find its RegionManager property (the one declared in the interface) set with their corresponding RegionManager each time they are injected in a Region. This should also include any view / view model injected in nested regions.

I hope this helps,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Jun 7, 2013 at 7:38 PM
Edited Jun 7, 2013 at 7:40 PM
Hi Damian,

Thanks for your response. It was indeed very helpful as always.
I did look at the RegionManagerAwareBehavior class, but I could get pass the issue by just using IRegionManagerAware interface of yours and setting the following in the view which has nested regions:
prism:RegionManager.RegionManager="{Binding TheRegionManager}"

Example:
<UserControl x:Class="UC1"
             xmlns:prism="http://www.codeplex.com/prism">
    <DockPanel>
        <ContentControl prism:RegionManager.RegionName="TOP_REGION"  
                        prism:ClearChildViewsRegionBehavior.ClearChildViews="True"
                        DockPanel.Dock="Top"
                        prism:RegionManager.RegionManager="{Binding TheRegionManager}"/>
        <ContentControl prism:RegionManager.RegionName="BOTTOM_REGION" 
                        prism:ClearChildViewsRegionBehavior.ClearChildViews="True"
                        DockPanel.Dock="Bottom"
                        prism:RegionManager.RegionManager="{Binding TheRegionManager}"/>
    </DockPanel>
</UserControl>
We are using ViewModel First apparoach and every viewModel implements IRegionManagerAware interface. In child view's OnVisualParentChanged method we do the following:
        protected override void OnVisualParentChanged(DependencyObject oldParent)
        {
            base.OnVisualParentChanged(oldParent);
            if (this.MyViewModel != null)
            {
                MyViewModel.TheRegionManager = RegionManager.GetRegionManager(this);
                // If TheRegionManager property is set on content control, then use this.Parent:
                if (MyViewModel.TheRegionManager == null && this.Parent != null)
                {
                    MyViewModel.TheRegionManager = RegionManager.GetRegionManager(this.Parent);
                }
            }
        }
We are in the process of studying the RegionManagerAwareBehavior and will probably move to it later (half hoping that prism's next release will have this fixed :-) ).