Nested Region within Popup Window

Topics: Prism v4 - WPF 4
Apr 2, 2013 at 2:01 AM
I have a popup window based on Damian's example here. The view that is created within this receives a new scoped region manager. My issue is that within my popup view, further down the tree I need to display two instances of a single sub-view (CommonView) bound to different data. Each instance needs to have its own scoped region manager.

Image

The documentation suggests handling this through view injection but I am unable to get the scoped region manager at the level of AView.

Using IRegionManagerAware, I am able to get the region manager within PopupView. I had hoped that AView would inherit its parent's region manager but the dependency property RegionManager.RegionManager is empty in AView. Adding IRegionManagerAware to AView doesn't help either as the PopupWindowAction doesn't reach that deep.

I also tried adding Damian's RegionManagerRegistrationBehavior (see here) but it never even sees the addition of AView.

Any ideas on how to best make this composition work? I figure I could pass the region manager down through a ViewModel but I'd rather find a way to do this simply through infrastructure or binding.

Thanks
Developer
Apr 4, 2013 at 6:36 PM
Hi,

I am glad you found both blog post useful for your application.

As a quick test I took the PopupWindowActionSample, modified it to include the RegionManagerAwareBehavior and implemented the IRegionManagerAware interface in the ClientView view, which is part of a UI composition similar to your description. In my case, the IRegionManagerAware.RegionManager property was populated correctly in the ClientView.

As a starting point, please check if the RegionManagerAwareBehavior is correctly registered in the ConfigureDefaultRegionBehaviors method of the bootstrapper. Also, it would be helpful to know which method you are using to add the AView to the ARegion and when (at startup, when clicking a button, etc) to have a better understanding of how your scenario is works.

Regards,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Apr 4, 2013 at 9:45 PM
Thanks for the response.

I was able to duplicate your success with the PopupWindowActionSample. Examining the details I see that ClientView is inserted into DetailsRegion via injection. In my case I'm inserting AView into ARegion automatically through discovery. This seems to make the critical difference.

When modifying the sample by registering DetailsRegion with ClientView, ClientView no longer receives the region manager through IRegionManagerAware.

Is this expected behavior?

Thank You...
Developer
Apr 5, 2013 at 7:20 PM
Hi,

You are right, the view injection and view discovery approaches add views at very different times. Hence it seems that the problem you are experiencing is mainly caused by a timing issue between the AutoPopulateRegionBehavior (in charge of the view discovery mechanism), the RegionManagerRegistrationBehavior (in charge of adding a region in the corresponding region manager) and the RegionManagerAwareBehavior.

Based on my understanding, the regions behaviors are attached to and executed in the regions in the order they were registered. By default in Prism, the AutoPopulateRegionBehavior is registered first, and hence, under some circumstances, it can add a view to a region before other behaviors are attached to it. For example, it can add a view before the RegionManagerAwareBehavior get to be attached to the region, and as a result the view's RegionManager property would not be set by it.

As a quick test, I tried changing the order in which the region behaviors are registered and as a result the RegionManager property could be set in a ClientView added through view discovery. To do this, you need to override the ConfigureDefaultRegionBehaviors method of the Bootstrapper, to do something like this:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
    // We add the RegionManagerRegistrationBehavior and RegionManagerAwareBehavior before the other behaviors
    IRegionBehaviorFactory behaviors = this.Container.Resolve<IRegionBehaviorFactory>();
    behaviors.AddIfMissing(RegionManagerRegistrationBehavior.BehaviorKey, typeof(RegionManagerRegistrationBehavior));
    behaviors.AddIfMissing(RegionManagerAwareBehavior.BehaviorKey, typeof(RegionManagerAwareBehavior));

    // The we invoke the base method to register the rest of the behaviors
    return base.ConfigureDefaultRegionBehaviors();
}
I also moved the registration of the RegionManagerRegistrationBehavior as we need it to execute first in order for the RegionManagerAwareBehavior to work correctly.

I hope this helps,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Apr 10, 2013 at 7:24 PM
Thank you once again for the quick response. It has taken me a few days to get around to trying this out.

With this solution I am indeed able to make the PopupWindowActionSample work as expected. However my real solution was still failing. The difference between my solution and the sample is that the Shell of my solution actually contains the PopupWindowAction and associated buttons etc. The sample initially directs all of this to a MainRegion.

Once again if I modify the sample in this way I am able to see the failing result. I feel that I'm dwelling in the subtleties of Prism's region management and appreciate your guidance.

Thanks
Developer
Apr 11, 2013 at 7:38 PM
Hi,

Indeed, I have tried moving the HelloWorldView code to the Shell window, and the RegionManagerAwareBehavior was unable to set the RegionManager property in the ClientsView. It seems that again, this is a timing issue regarding the initialization of the components of Prism's region system.

Although so far I am unable to point the exact cause of the timing problem this time, the good news is that we have several workarounds we can apply to this. Some have been already discussed in this thread before:

  • Add the ClientsView after "initialization." (e.g. not by using view discovery)
  • Define the InteractionRequestTriggers in a view that will be injected in a region instead of defining them directly in the Shell.
Also, another approach can be to remove and re-add the views in the DetailsRegion (where the ClientsView is injected). This will trigger some behaviors, like the RegionManagerAwareBehavior, to execute again. A good place to do this is, for example, the setter of the RegionManager property in the SelectClientViewModel:
        // IRegionManagerAware implementation
        public IRegionManager RegionManager {
            get
            {
                return this.regionManager;
            }

            set
            {
                this.regionManager = value;

                // We remove and re-add the views to trigger the behaviors again.
                // This might break some views / view models that have logic that execute in response 
                // to them being add / removed / activated / deactivated in a region.
                List<object> views = new List<object>();
                foreach (var view in this.regionManager.Regions["DetailsRegion"].Views)
                {
                    views.Add(view);
                }

                foreach (var view in views)
                {
                    this.regionManager.Regions["DetailsRegion"].Remove(view);
                }

                foreach (var view in views)
                {
                    this.regionManager.Regions["DetailsRegion"].Add(view);
                }
            }
        }
However, take into account that this approach might not be recommended for all scenarios.

Regards,

Damian Cherubini
http://blogs.southworks.net/dcherubini
Apr 16, 2013 at 5:03 PM
Thanks again for the reply. I will work with these workarounds.
Apr 29, 2013 at 10:02 PM
I've hit another snag while using the RegionManagerAwareBehavior.

I'm using the above second workaround. i.e. Define the InteractionRequestTriggers in a view that will be injected in a region instead of defining them directly in the Shell.

I find that if a ContentControl which declares a region is added to the Shell or in the view that contains the PopupWindowAction i.e HelloWorldView, ClientView once again doesn't receive an IRegionManager via the RegionManagerAwareBehavior.

Originally we noticed this from the Shell but the problem is generalized. I'm concerned that the only solution is view injection which is not ideal in my case. Any other ideas on this issue? I wouldn't mind injecting the views into the regions associated with the PopupWindowAction but I don't see an obvious way to do that. The RegionManager associated with the view containing the PopupWindowActions doesn't contain these regions.

Thanks again.
Developer
Apr 30, 2013 at 8:59 PM
Hi,

I was able to reproduce the scenario you described. Indeed, this doesn't seem to be appear only in the Shell, but in any part of the application containing the popup, depending on certain factors. It seems that this problem is a timing issue between the RegionManagerAwareBehavior, the PopupWindowAction and several of the default region behaviors provided by Prism for the particular scenario of using view discovery in an inner region inside a popup defined as the action on an InteractionRequestTrigger. Basically, in some cases, the inner region (e. g. the DetailsRegion) is created before the popup is shown, which causes the view registered to be injected in said region long before the RegionManager is set in the popup by the PopupWindowAction.

In my opinion, the convenient approach to work around this is to inject the views after the PopupWindowAction shows the popup. A possible approach to do this could be to inject the views in the setter of the SelectClientViewModel's RegionManager property. There, we can be sure that the RegionManager is available.

How to do this will depend on your preferences. For example, instead of registering the view types using RegisterViewWithRegion in the modules, you could register the view types in a custom Shared Service. Then, in the setter of the aforementioned property, you could simply ask the service to inject the registered views (if any) of the DetailsRegion, which in turn could instantiate the views through the ServiceLocator. Like this, the views would be still registered by their corresponding modules and the SelectClientViewModel would not be responsible of injecting the views itself.

I hope this helps.

Damian Cherubini
http://blogs.southworks.net/dcherubini
May 1, 2013 at 5:00 PM
Indeed it does. Thank You.