Problem when remove view from region

Topics: Prism v4 - WPF 4
Sep 16, 2011 at 3:57 PM
Edited Sep 16, 2011 at 5:08 PM

Hi,

I have a shell which contains single region that used to load different views. So I need to remove the current view so the memory can be released and I can load a different view. Since this is a hosted class library I want to do the remove when the shell is closed. So in my Shell.xam.cs, I call "RemoveViews" on Shell_Closing:

 private void RemoveViews()
 {
       _region = _regionManager.Regions["MainRegion"];
        if (_region != null)
            {
                foreach (object view in _region.Views)
                {
                    _region.Remove(view);
                }
            }         
}

The line "_region.Remove(view)" will throw error "The region does not contain the specified view.", even "view" is returning my view type (ModuleAView) correctly, in this case the view is my ModuleAView which decorate with [Export(typeof(ModuleAView))]. The view also implement IRegionMemberLifetime and have "keepAlive" property return to false.

  What is wrong with the code? Is this the right way to remove view?

Thanks!

 

 

Developer
Sep 16, 2011 at 6:27 PM

Hi,

We haven't received similar issues before, as removing views after closing the shell is not a common scenario in Prism.
Therefore, it would be helpful if you could provide us with a repro sample application that portrays that problem, so we can help you find the cause and a possible workaround for it.

Agustin Adami
http://blogs.southworks.net/aadami

Sep 20, 2011 at 12:24 PM

Sorry for the delayed response. I've been trying to put together a repro sample app for you to look at. Because my class library hosted in a commercial Win32 (arcmap.exe) application, so I have to use a WPF app to pretend to be the host app. My class library implements an extension for the host app with bunch of tools/ commands. In my sample app, there are 2 buttons on the host window and each should bring up a shell window and I will need to load different views in one of the shell and unload the view when shell is closed or the view is navigated away.

The sample app (WPFRemoveViewTest.zip) is at https://skydrive.live.com/redir.aspx?cid=4f0ce6c357477187&resid=4F0CE6C357477187!121

Thanks a lot for offering the help!

Developer
Sep 20, 2011 at 8:13 PM

Hi Julie,

I think that this exception is mostly caused because the Remove method of the region is called twice:

It's possible that when you remove the view manually (in the RemoveViews method of Shell1) the region manages to remove the view successfully. However, as your view (ModuleAView) implements IRegionMemberLifetime, it gets notified that it's not the active view anymore and then, as the KeepAlive property returns false, Prism tries to remove the view from the region.

So as the view is already removed, the view cannot be found inside the region and the exception is thrown.

To prevent this from happening you could use one of the following approaches:

  • Remove the views using the Remove method of the region, but the KeepAlive property of the views should return true (or the views shouldn't implement IRegionMemberLifetime at all).
  • Deactivate the views using the Deactivate method of the region and, if the KeepAlive property of views return false, Prism will remove them from the region.

If not all of your views' KeepAlive property return true or false, you could have a combination of both approaches: first, try to deactivate all the views (the ones with KeepAlive false should be removed) and then iterate through the view collection again and remove the views that are still in the region (those should be the ones with KeepAlive true).

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Sep 21, 2011 at 1:27 PM

Hi Darmian,

Thans for the help! Following are some findings and questions.

1. I was judging if a view is unloaded by checking if "InitializeComponent" of ModuleA is called again when "Open Shell 1" button is clicked again. Is this the right approch?

2. If the answer is "yes", then I was able to use Deactivate of the region and KeepAlive = false to unload the ModuleAView (or decorate ModuleAView with "[RegionMemberLifetime(KeepAlive=false)]", and ModuleAView need to have "CreationPolicy.NonShared". True?

3. When ModuleAView is unloaded, ModuleAViewModel is not, because it is "Shared" and was never "unloaded". The result is that if a control on ModuleAView is binding to the data on ModuleAViewModel, that control content will still be there even ModuleAView is re-initialized (I tested, if a control is not binded, it will be cleared out when the view is re-opened). So is that to say, if I need a real memory release if ModuleAView is not needed anymore, I shuld also unload ModuleAViewModel?

4. What is the best practice in this senario that I need the shell to host many views, one at a time?

Thank you very much!

 

Developer
Sep 21, 2011 at 9:39 PM

Hi Julie,

I believe that the approach you mentioned about using the invocation of the InitializeComponent method in the ModuleAView to check if the view has been unloaded before could cause some problems in some scenarios. For example, if you have to instantiate the view more than once (e.g. in a tabbed control) the InitializeComponent will be invoked in each view and this approach wouldn't work properly.

Another approach for this would be to simply check the if the view is in the region or not through the Views property of the region.

Regarding the ModuleAViewModel, if it's declared as a singleton (based on my understanding) the view model will not be disposed as the container will hold a reference to the "singleton" instance. As there should only be one instance of the ModuleAViewModel, this should not create a big memory load. However, if you wish to dispose the view model when the view is destroyed, it may make sense to declare the ModuleAViewModel with the "NonShared" attribute.

Also, based on my understanding of your scenario, it seems like a common view-based navigation approach should work just fine. When you need to show a view in the shell and it already have another view, the previous view should be disposed automatically if its KeepAlive method returns false.

You can find more about view-based navigation in the following link:

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini