how to create multi-shell window application using Prism 4.0 and MEF with MVVM

Topics: Prism v4 - WPF 4
Jan 4, 2013 at 7:23 AM

I modified your stocktraderRI, trying to create a multiple shell mvvm mef application(stocktraderRI is only single shell). I was able to create and display a second shell(actually even the 3rd, 4th shell, etc), but I couldn't have any view displayed to the new shells. All of my new views have

[ViewExport(RegionName = RegionNames.ARegionName)]

[PartCreationPolicy(CreationPolicy.NonShared)]   

public partial class MyView : UserControl

{

}

statement in code behind of that view class.

 

And all the shells of course specify its regions using the corresponding region name in .xaml file. Each shell also has its own new region manager. The issue is how can each view specify not only the region name, but also the corresponding region manager used by a shell which has that region?

 

Here is my code to create the new shell:

 

 IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

var newRegionManager = regionManager.CreateRegionManager();

var newShell = new MyNewShell(newRegionManager);

RegionManager.SetRegionManager(newShell, newRegionManager);

RegionManager.UpdateRegions();

newShell.Show();

 

Here is the xaml code in the new shell that specify the region name:

 

 <ContentControl Name="something" cal:RegionManager.RegionManager="{Binding RegionManager, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" cal:RegionManager.RegionName="{x:Static inf:RegionNames.ARegionName}" />

 

So is it possible for a view to specify a new region manager to use for that region? If yes, how?

 

In my bootstrapper, I already have the corresponding module loaded.

Any help deeply appreciated. Feel free to ask any more question.

 

 

Developer
Jan 4, 2013 at 6:40 PM

Hi,

Based on my understanding, using the ViewExport attribute approach to register views will not allow you to specify a custom RegionManager, this can be seen in the AutoPopulateExportedViewsBehavior which discovers the views exported in the container with this attribute and automatically populates them into the associated region. In which case if this behavior was registered by overriding the ConfigureDefaultRegionBehaviors in the bootstrapper, it will be attached to each region by default no matter if they are in different RegionManagers unless this behavior is only added to an specific region. After running some tests I could verify that the views registered with this approach were added to regions in different region manager from different windows.

Based on this, I believe that if you need explicit control on which RegionManager the views will be added then the recommended approach is using View Injection in which case the views should be added programmatically instead of using declarative attributes (you can find more information about this in Chapter 7: Composing the User Interface of the Prism documentation). When following this approach, you will need to define some logic to retrieve each RegionManager attached to each window. This could be done for example by using a shared service, or if you can retrieve each instance of the created windows you could access the RegioManager property where you stored each corresponding RegionManager.

On the other hand, as you may find using the View Discovery approach may also not be adequate to achieve this kind of scenarios as out of the box. Although as mentioned in the following work item, some changes could be made to use this approach with an specific RegionManager:

I hope you find this handy,

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

Jan 7, 2013 at 5:17 PM

Hi Agustin,

Thanks a lot for the answer. But I followed your suggestion to use View Injection after reading thoroughly Chapter 7, but always got an exception:

Activation error occured while trying to get instance of type MyView, key ""

Here is the code to lanuch MyView and got that exception:

IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();           

var newRegionManager = regionManager.CreateRegionManager();

var newShell = new MyNewShell(newRegionManager/*, neweventAggregator*/);           

RegionManager.SetRegionManager(newShell, newRegionManager);           

RegionManager.SetRegionName(newShell.DemoRegion, RegionNames.ARegionName);           

RegionManager.UpdateRegions();           

// View injection           

IRegion region = newRegionManager.Regions[RegionNames.ARegionName];
                     

MyView newView = ServiceLocator.Current.GetInstance<MyView>(); //this line caused Activation error occured while trying to get instance of type MyView, key ""

// MyView newView = new MyView();    //this line will not cause exception but won't cause the constructor of view model referenced by this view to be executed thus unusable.      

region.Add(newView, "NewView");           

region.Activate(newView);

 newShell.Show();

 

I have made sure MyView assembly is loaded in bootstrapper, and it has either [Export] or [ViewExport] declaration in the code behind file for MyView class so MEF should be able to find it.

Really appreciate more help from you. Thank you in advance.

Developer
Jan 7, 2013 at 8:39 PM

Hi,

Based on my understanding of your details, the error you mentioned is more related to a MEF composition problem, than to the use of multiple region managers. In my experience this kind of exception may arise by multiple causes. For example, as a starting point you could check if this is related to one of the following causes:

  • By a timing issue when calling the ConfigureAggregateCatalog method, you should make sure that, when you attempt to retrieve the MyView instance, this method has already been called.
  • A class being exported in the container more than once, based on my understanding, when using MEF, if you have a project that exports types into the container, this project should be referenced with Copy Local = True in only one project, and with Copy Local = False in the rest. This is the case, for exampling, when using the Microsoft.Practices.Prism.MefExtensions assembly.
  • Missing dependencies, when resolving the instance from the container (e.g if the view tries to retrieve an instance through an interface that has not being mapped in the container to an specific implementation).

If you can't find the reason of this unexpected behavior, it would be helpful if you could provide us with more detailed information regarding your scenario, for example the stack trace of this exception, and its inner exceptions, what is the scope of the code you posted above (if you are calling this in a module...), etc.

Best Regards,

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

Jan 8, 2013 at 6:30 PM

Hi Agustin,

I make sure ConfigureAggregateCatalog in bootstrapper is called before attempting to retrieve the MyView instance.

I make sure MyView project is referenced with Copy Local=True only once in my main project(I changed to Copy Local = false and the bootstrapper simply complains it can't find it in ConfigureAggregateCataglog() call.

I make sure there is not other dependency in MyView class(I even commented out its view modal class to be safe)

and still got that activation exception for MyView in that line, here is exception detail:

Microsoft.Practices.ServiceLocation.ActivationException was unhandled by user code  Message=Activation error occured while trying to get instance of type TechnicalAnalysisView, key ""  Source=Microsoft.Practices.Prism.MefExtensions  StackTrace:       at Microsoft.Practices.Prism.MefExtensions.MefServiceLocatorAdapter.DoGetInstance(Type serviceType, String key) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism.MefExtensions\MefServiceLocatorAdapter.cs:line 76       at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key)  InnerException: 

 

What other infomation do you need in order to help me? Where can I send it if you want? Thank you so much.

Developer
Jan 8, 2013 at 8:06 PM

Hi,

Sadly, the stack trace from the exception does not provide any helpful information to troubleshoot this error. We were expecting to find more details about where the composition process is failing inside the inner exceptions.

Therefore, it would be helpful if you could isolate this problem into a repro-sample application that we could analyze to help you find the cause behind this problem. You could update your repro-sample for example in SkyDrive, or in the file hosting site of your preference.

Regards,

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

Jan 11, 2013 at 3:22 AM

Thank you very much guys. After I double checked the code, it's because I commented out  the [Export] declaration in MyView class during debugging. Now View Injection works as you suggested. Really appreciated your guys' great help.