Search code examples
c#silverlightsilverlight-4.0prismmef

Prism 4 - locally scoped RegionManager


I have silverlight 4 application with PRISM 4, I'm using MEF.

My Shell defines one main region in which modules are loaded, I want modules to have their own RegionManager, so regions that they define are places in local RegionManager instead of global. And I want this local RegionManager to be resolved by container (for type IRegionManager) when inside the module.

However the method from documentation:

IRegion detailsRegion = this.regionManager.Regions["DetailsRegion"];
View view = new View();
bool createRegionManagerScope = true;
IRegionManager detailsRegionManager = detailsRegion.Add(view, null, 
                            createRegionManagerScope);

Doesnt work for me, when resolving IRegionManager from inside child view I still get GlobalRegionManager.


Solution

  • I was in the same situation as you, I had a nested region manager, but all of the child views were still getting the global region manager. I came up with what I consider a reasonable solution.

    First I define an interface:

    /// <summary>
    /// An interface which is aware of the current region manager.
    /// </summary>
    public interface IRegionManagerAware
    {
        /// <summary>
        /// Gets or sets the current region manager.
        /// </summary>
        /// <value>
        /// The current region manager.
        /// </value>
        IRegionManager RegionManager { get; set; }
    }
    

    Then I setup a RegionBehavior like so:

    /// <summary>
    /// A behaviour class which attaches the current scoped <see cref="IRegionManager"/> to views and their data contexts.
    /// </summary>
    public class RegionAwareBehaviour : RegionBehavior
    {
        /// <summary>
        /// The key to identify this behaviour.
        /// </summary>
        public const string RegionAwareBehaviourKey = "RegionAwareBehaviour";
    
        /// <summary>
        /// Override this method to perform the logic after the behaviour has been attached.
        /// </summary>
        protected override void OnAttach()
        {
            Region.Views.CollectionChanged += RegionViewsChanged;
        }
    
        private void RegionViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Contract.Requires<ArgumentNullException>(e != null);
    
            if (e.NewItems != null)
                foreach (var item in e.NewItems)
                    MakeViewAware(item);
        }
    
        private void MakeViewAware(object view)
        {
            Contract.Requires<ArgumentNullException>(view != null);
    
            var frameworkElement = view as FrameworkElement;
            if (frameworkElement != null)
                MakeDataContextAware(frameworkElement);
    
            MakeAware(view);
        }
    
        private void MakeDataContextAware(FrameworkElement frameworkElement)
        {
            Contract.Requires<ArgumentNullException>(frameworkElement != null);
    
            if (frameworkElement.DataContext != null)
                MakeAware(frameworkElement.DataContext);
        }
    
        private void MakeAware(object target)
        {
            Contract.Requires<ArgumentNullException>(target != null);
    
            var scope = target as IRegionManagerAware;
            if (scope != null)
                scope.RegionManager = Region.RegionManager;
        }
    }
    

    Then apply this to all regions in your bootstrapper:

    protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
    {
        var behaviours = base.ConfigureDefaultRegionBehaviors();
    
        behaviours.AddIfMissing(RegionAwareBehaviour.RegionAwareBehaviourKey, typeof(RegionAwareBehaviour));
    
        return behaviours;
    }
    

    Then all you have to do is implement IRegionManagerAware on your view/viewmodel, probably like so:

    public class MyView : IRegionManagerAware
    {
        IRegionManager RegionManager { set; get; }
    }
    

    Then when this view is added to a region, the behaviour will correctly set the RegionManager property to the currently scoped region manager.