What's the difference between the two RegisterViewWithRegion overloads in Prism

Topics: Prism v4 - WPF 4
May 3, 2011 at 8:48 PM

I'm using Prism4 and in one of my modules I'm trying to register a view with a region and also handle its button clicked event (which is published when user clicks on a button on the view).

public class MyModule : IModule
{
    private readonly IUnityContainer container;
    private readonly IRegionManager regionManager;
    private readonly IEventAggregator eventAggregator;

    public MyModule(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator)
    {
        this.container     = container;
        this.regionManager = regionManager;
        this.eventAggregator = eventAggregator;

        eventAggregator.GetEvent<ViewAButtonClicked>().Subscribe(ViewAButtonClicked);
    }

    public void Initialize()
    {
        this.regionManager.RegisterViewWithRegion("MainRegion", typeof(ViewA));

        // this 2nd overload would work fine
        //this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<ViewA>());            
    }

    public void ViewAButtonClicked()
    {
       // some handling code 
       // does *NOT* execute if using the 1st RegisterViewWithRegion overload
       // executes if using the 2nd RegisterViewWithRegion overload
    }
}

The above code works fine up except that it does not execute the ViewAButtonClicked method.

If I switched to use the other overload (commented out in code above) then everything works as expected, the ButtonClicked method runs.

The descriptions from msdn are very similar and I'm not sure why it's giving me the different behavior described above. Why does one work and one doesn't when it comes to handling the button click event?

RegisterViewWithRegion(IRegionManager, String, Func<(Of <(Object>)>)): Associate a view with a region, using a delegate to resolve a concreate instance of the ? view. When the region get's displayed, this delelgate will be called and the result will be added to the views collection of the region.

RegisterViewWithRegion(IRegionManager, String, Type): Associate a view with a region, by registering a type. When the region get's displayed this type will be resolved using the ServiceLocator into a concrete instance. The instance will be added to the Views collection of the region.

May 3, 2011 at 9:56 PM

Hi,

You could try to set the keepSubscriberReference alive parameter to true on the Subscribe method, when you specify that a strong reference should be maintained between your event and its subscriber. Therefore, you will grant that the subscriber won't be garbage collected until you manually unsubscribe to that event.

In your case, since your subscriber is the MyModule, that's the one that won't be garbage collected until you've unsubscribed from the event.

On the other hand, a weak event subscription won't hold a strong reference to the Subscriber, hence it will be active as long as the subscriber is kept alive. For example, if you subscribe to an event from within your MyModule, if you don't have any other references to it, that instance will eventually be garbage collected. Note that it's usual to have the IModule be garbage collected.

Please let me know if this information helps you.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

 

May 3, 2011 at 11:11 PM
Edited May 3, 2011 at 11:16 PM

Thanks so much mbronzovic, setting keepSubscriberReference to true fixed it and now I can use either overloads. I'm still a little confused as to why the 1st overload would require such a parameter and the 2nd overload doesn't, is it because the 2nd overload (the one using the delegate) somehow prevents ModuleA from garbage collected? If so how?

Another question: which overload/convention is better/preferable?

Developer
May 4, 2011 at 3:28 PM
Edited May 4, 2011 at 3:28 PM

Hi,

When you call the RegisterViewWithRegion(string regionName, Type viewType) overload, the service locator is used to obtain an instance of the view type, and then the RegisterViewWithRegion(string regionName, Func<object> getContentDelegate) overload is called, the getContentDelegate pointing to a method that uses the service locator to retrieve the instance of the view type specified (as defined before). The region name, along with the getContentDelegate are stored in a ListDictionary, which are then used to create the view when the region specified is added to the visual tree.

In the example you've mentioned, the delegate you're specifying contains a reference to a field of your MyModule class, which is therefore kept alive. If you used the second overload like this:

this.regionManager.RegisterViewWithRegion("MainRegion", () => ServiceLocator.Current.GetInstance<ViewA>());

Your module wouldn't be kept alive.

However, you should take into account that the usual is to have your module class be garbage collected, and instead, subscribe to events and perform additional operations in a controller class, such as a ViewModel or a service.

As for which overload to use, the decision depends entirely on your personal preference and which one suits better your specific case.

I hope you find this helpful.

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

May 4, 2011 at 3:37 PM
Edited May 4, 2011 at 4:21 PM

Thank you GuidoMaliandi! What you explained makes perfect sense. I also tried the overload with ServiceLocator as specified and confirmed that the module isn't kept alive that way either.

I will take your advice and move the subscription in my viewmodel or service instead.