How to create a separate XAP file using Prism/MEFBootstrapper for a complex Child Window?

Topics: Prism v4 - Silverlight 4
Nov 28, 2012 at 6:15 PM

I originally posted this on the new Silverlight forum, but was told that I should post my question here. I am using Silverlight 5, but only saw a Silverlight4/Prism 4 topic, which is where I have posted this question.

A similar question was asked on the Silverlight forum in 2011, but the link does not contain definitive answers to my questions. The old link named, "How to Register a ChildWindow with PRISM since it is not a region but a pop up", is located at http://social.msdn.microsoft.com/Forums/en-US/silverlightwcf/thread/4c069cd1-3a44-476b-9e8c-64473af65cc8/.

I have a sophisticated Child Window form wizard that has many tab views (~20+).

  • Each tab view has its own ViewModel.
  • The different views are required to handle different user flows.
  • All flows use two common tabs and typically two additional tabs.
  • The additional tabs are determined by the user's choices.
  • Most users will use 1 or 2 wizard flows max.

The form wizard is currently implemented as part of the main application. It is adding substantially to the download size. The main application functionality is used about 90-95% of the time. The wizard is used about 5-10% of the time, and only about 20% of the wizard functionality is used by any user during a session. Prism seems like a good fit to strip out the wizard functionality and make it available on demand.

  • What I would like to do is to create a XAP file that contains the Child Window wizard and 2 tabs that are used for all use models.
  • I would like to download separate tab controls (in their own XAP files) on demand based upon the user's choices.

I have been working to understand the Prism/MEFBootstrapper methodology. I have simple module regions working in the Shell. However, I am at a loss as to how one registers and invokes a Child Window in a separate XAP file.

I will also need the Child Window to load the different tabs from the appropriate XAP files, but I am assuming that the implementation will follow the pattern of inserting regions into the main Shell application. Is my assumption correct adding regions to tabs on the Child Window?

Please let me know of any links, examples, or books that might cover this particular subject in more detail.

Colin Blair, on the Silverlight Forum, replied as follows to my question on that forum.

"As for registering and invoking a child window, that would probably involve using the base ChildWindow class in the caller in combination with an interface that can be used by the caller and implemented by the Child Window itself. That way there are no direct references from the caller to the Child WIndow's assembly."

I am new to Prism, and do not have enough knowledge to act upon Colin's suggestion. Any tips or suggestions would be greatly appreciated...

I am using Mark Seemann's Dependency Injection in .NET book as my primary reference, but have many chapters to go to complete his book.

Thanks...

Developer
Nov 28, 2012 at 8:10 PM
Edited Nov 28, 2012 at 8:12 PM

Hi,

I believe the are several approaches that could be followed to obtain the functionality you are describing.

For example, a possible approach could be to move the WizardChildWindow to a module (for example, a WizardModule) and export it through an interface defined in a common project (for example an Infrastructure project):

[Export(IWizardChildWindow)]
public class WizardChildWindow: ChildWindow, IWizardChildWindow

Hence, when you need to use the wizard, you just need to check if the WizardModule is loaded or not. If the module WizardModule is loaded, then you can obtain the WizardChildWindow using the ServiceLocator and invoke a Show method to show it:

IWizardChildWindow wizard = ServiceLocator.Current.GetInstance<IWizardChildWindow>();
wizard.Show();

If the module is not loaded, you will need to load the WizardModule on demand and wait the module to be loaded (you can do this using the LoadModuleCompleted event of the IModuleManager) before obtaining the WizardChildWindow. You can know if the module has been loaded or not by iterating the Modules collection of the IModuleCatalog in order to find the ModuleInfo of the WizardModule and check if its ModuleState property is set as "Initialized" (which means the module is loaded).

Take into account that in order to use regions in the WizardChildWindow you will need to set a RegionManager manually. For example, you can do this in the WizardChildWindow's constructor:

[ImportingConstructor]
public WizardChildWindow(IRegionManager rm)
{
    // If you want to use the main region manager of your application
    rm.SetRegionManager(this, rm);

    // Or, if you want to use a scoped region manager
    rm.SetRegionManager(this, rm.CreateRegionManager());
}

Finally, you can do something similar to load the modules that contain the tabs of the wizard on demand when needed.

You can find more information about this in the following chapters of the Prism documentation:

I hope you find this useful,

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

Nov 28, 2012 at 9:39 PM
Edited Nov 28, 2012 at 10:13 PM

Hi DCherubini,

Thank you for your quick and informative response.

I have a smaller test case using a Silverlight app project (SharedForms). This project contains 2 ChildWindows that are used throughout the main and supporting application modules for messaging, debug and error reporting.

I have followed the declaration pattern for each.

[Export(typeof(ISMMMessageWindow))]
public partial class SMMMessageWindow : ChildWindow, ISMMMessageWindow
{...
}

[Export(typeof(IErrorWindow))]
public partial class ErrorWindow : ChildWindow, IErrorWindow
{...
}

I have assumed that it is okay to have 2 such controls in a common XAP file and that either can be exported. Is this correct?
 
In the SharedForms project I added the class shown below. Will this work? Is something like this required to access the 2 ChildWindow controls?

[Export(typeof(IModule))]
public class SharedFormsModule: IModule
{
 IRegionManager regionManager;
 public SharedFormsModule(IRegionManager regionManager)
 {
  this.regionManager = regionManager;
 }

 public void Initialize()
 {
  regionManager.RegisterViewWithRegion("ErrorWindowRegion", typeof(IErrorWindow));
  regionManager.RegisterViewWithRegion("SMMMessageBoxRegion", typeof(ISMMMessageBox));
 }
}

Thanks, Warren

Nov 29, 2012 at 5:17 AM
Edited Nov 29, 2012 at 5:18 AM

Hi,

I would not go with the ServiceLocator to import the childwindow. And also if you Export your childwindow as IErrorWindow/ISMMMessageWindow you will not be able to call Show() on it as that is a method on the ChildWindow.

I would do the following

 

[Export("SMMMessageWindow", typeof(ChildWindow)]
public partial class SMMMessageWindow : ChildWindow, ISMMMessageWindow
{...
}

[Export("ErrorWindow", typeof(ChildWindow)]
public partial class ErrorWindow : ChildWindow, IErrorWindow
{...
}


[Export(typeof(IModule))]
public class SharedFormsModule: IModule
{
 IRegionManager regionManager;
 public SharedFormsModule(IRegionManager regionManager)
 {
  this.regionManager = regionManager;
 }

 [Import("SMMMessageWindow")]
 public ChildWindow ErrorWindow {get; set;}

 [Import("ErrorWindow")]
 public ChildWindow SMMMessageWindow {get; set;} 
}

 

Now you can call the show on your childwindows when you want, and yes the viewmodels for your childwindows would also be available as you would have injected into their constructors using a ImportingConstructor.

Further you could do some clever stuff by using the InteractionRequest<> of Prism.Interactivity to invoke your childwindow instead of directly calling Show() on them.

Hope this helps.

Cheers!

Nov 29, 2012 at 7:30 PM

Hello,

I have spent some time reading articles/references and reviewing your advice. What you said about exporting the two classes as type ChildWindow makes sense. For now I will use Show() for the ChildWindows, but, I am definitely interested in the InteractionNotificationRequest approach after I get the basic module flows working.

I have some additional questions.

  1. If I understand the intent, importing each child window class into the SharedFormsModule class makes it available for use by the importing class in the main application shell. Is this correct?
  2. IModule requires an instance of Initialize(). Is this method blank or does it require some additional code?
  3. I am using a customized version of the Silverlight Navigation project as my application template. I would like to add the module code on demand. What do I have to do in the shell (e.g., a Home page or some other page) to access the module from a page since it is not being used as a region? All the explanations/examples that I have found only deal with regions.

I have created a ModulesCatalog.xaml as follows:

<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
 <Modularity:ModuleInfoGroup InitializationMode="OnDemand">
  <Modularity:ModuleInfo Ref="SharedForms.xap"
          ModuleName="SharedFormsModule"
          ModuleType="SharedForms.SharedFormsModule, SharedForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
 </Modularity:ModuleInfoGroup>
</Modularity:ModuleCatalog> 

The bootstrapper appears to be working properly. The shell is being created by the bootstrapper, and the modules catalog is being created/returned in the following code.

protected override IModuleCatalog CreateModuleCatalog()
{
 var moduleCatalog = Modularity.ModuleCatalog.CreateFromXaml(new Uri("/SMMApp;component/ModulesCatalog.xaml", UriKind.Relative));
 return moduleCatalog;
}

I am missing the steps to get/use the module child window code in one of the pages of the shell.

Thanks, Warren

Nov 29, 2012 at 8:42 PM
Edited Nov 29, 2012 at 8:44 PM

Hello gan_s,

After I import IModuleManager, do I use something similar to the following code to load the module on demand?

ModuleManager.LoadModule("SharedFormsModule" );

ModuleManager.LoadModuleCompleted += (s, ev) =>
{...
};

I'll give this a try to see if I can work through the process.

Thanks, Warren

Nov 30, 2012 at 3:03 AM

Thats right Warren. You need to call the LoadModule with the module name defined in your modulescatalog.xaml. Once your module is downloaded you would use regionManager.RequestNavigate to load the view in your MainRegion or whatever the name of the region is in your shell.

From what I understand in your scenario you only have the 2 childwindows in the SharedFormsModule? Then in that case wherever you have an import on the ChildWindows you'll have those properties available for use. With regards to the Initialize() event in your IModule you wouldnt need to do anything. Ideally you would use it to register views with regions, which is usually needed to be done when you use a Unity container for managing your views and viewmodels. In case of MEF implementation you dont need to worry about it. You could plug some initialzation logic for the module in that method, if there is any. Otherwise can be left blank.

The silverlight navigation framework provides you with events like Navigated/NavigationFailed etc. You can hook up to the Navigated event to download your shared module. Also keep in mind you might want to check the module state before calling a LoadModule. Reason being you would download it only once and it will be available in the AggregateCatalog else you would get ugly MEF errors saying the exports could not be recomposed and stuff. So next time around you wont need to download the xap again. Do something like this

var module = ModuleCatalog.Modules.Single(m => m.ModuleName.Equals("SharedFormsModule"));

// Check state of the module.
if (module.State == ModuleState.NotStarted)
   ModuleManager.LoadModule(module.ModuleName);

Where ModuleCatalog is an implementation of IModuleCatalog (import it in your constructor just like IModuleManager).

Cheers!

Nov 30, 2012 at 7:12 PM

 Hello,

Thanks much for your assistance. I have made some progress today.

 

I struggled for quite a while with a module not found error where VS reported that the named module could not be found. However, after checking everything in the Silverlight and SharedFormsModule code and doing some web research, I found that I had a mismatch between the importing annotation on the property in the SharedFormsModule and the class exporting annotation as follows:

 

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow ErrorWindow { get; set; }

 

and

 

[Export(typeof(ChildWindow))]

public partial class ErrorWindow : ChildWindow, IErrorWindow

 

When I added the name to the export, I was able to read the module. If there are several layers of imports/exports in a given module on the server, is there a way to get better debugging information using MEF? The loaded module was found, but there was an exception thrown somewhere in the Load code because of the underlying name mismatch, but there was not mention in the error exception of the real problem.

 

Lastly, after the ModuleManager.LoadModuleCompleted event fires, which it does now, how do I get an instance of the downloaded module to access either one of the two ChildWindows? I have searched on the net, but not found any help here.

 

Thanks, Warren

Dec 1, 2012 at 5:00 PM

The only way to get the instances of the childwindos is by importing them. What are you trying to achieve anyway?

Regarding debugging info on xap/mef erros you need to check the error property on the module downloaded event args.

On 1 Dec 2012 01:42, "highdownts" <notifications@codeplex.com> wrote:

From: highdownts

Hello,

Thanks much for your assistance. I have made some progress today.

I struggled for quite a while with a module not found error where VS reported that the named module could not be found. However, after checking everything in the Silverlight and SharedFormsModule code and doing some web research, I found that I had a mismatch between the importing annotation on the property in the SharedFormsModule and the class exporting annotation as follows:

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow ErrorWindow { get; set; }

and

[Export(typeof(ChildWindow))]

public partial class ErrorWindow : ChildWindow, IErrorWindow

When I added the name to the export, I was able to read the module. If there are several layers of imports/exports in a given module on the server, is there a way to get better debugging information using MEF? The loaded module was found, but there was an exception thrown somewhere in the Load code because of the underlying name mismatch, but there was not mention in the error exception of the real problem.

Lastly, after the ModuleManager.LoadModuleCompleted event fires, which it does now, how do I get an instance of the downloaded module to access either one of the two ChildWindows? I have searched on the net, but not found any help here.

Thanks, Warren

Read the full discussion online.

To add a post to this discussion, reply to this email (CompositeWPF@discussions.codeplex.com)

To start a new discussion for this project, email CompositeWPF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Dec 2, 2012 at 1:08 AM

 

Hello,

 

I have created a small test solution to debug the Prism/Child Window code. I believe the MEFBootstrapper is working properly. The Shell code executes to the LoadModuleCompleted event shown in the code below. You mentioned that the only way to use the exported child windows is to import them. Are you saying that I need to add a hardcoded dependency to import either of the two child windows? Is there a way to use Prism to access the module and then its two properties without adding a dependency? I tried adding a library reference to the SharedForms project and importing (one at a time) the SharedFormsModule and then the two child window properties, but this does not seem to work. Can you please explain how to import the two child windows?

 

For example, I get an exception using the following syntax for the module or its properties.

 

[Import(typeof(SharedFormsModule))]

public SharedFormsModule SharedFormsModule;

 

[Import(typeof(IModule))]

public IModule errowWindow;

 

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow errowWindow;

  

This is the Shell code.

 

[ImportingConstructor]

public Shell(IModuleManager ModuleManager, IModuleCatalog moduleCatalog)

{

InitializeComponent();

this.ModuleManager = ModuleManager;

this.moduleCatalog = moduleCatalog;

this.Loaded += (s, ev) =>

{

var module = moduleCatalog.Modules.Single(m => m.ModuleName.Equals("SharedFormsModule"));

if (module != null && module.State == ModuleState.NotStarted)

ModuleManager.LoadModule("SharedFormsModule");

ModuleManager.LoadModuleCompleted += (sn, evt) =>

{

MessageBox.Show("Module Loaded...");

 

// ... How to access either of the two child windows???

 

};

};

}

 

This is the child window project.

 

namespace SharedForms

{

[Export(typeof(SharedFormsModule))]

public class SharedFormsModule: IModule, ISharedFormsModule

{

public void Initialize()

{

}

[Import("SMMMessageWindow", typeof(ChildWindow))]

public ChildWindow SMMMessageWindow { get; set; }

 

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow ErrorWindow { get; set; }

}

}

Dec 2, 2012 at 6:04 AM

Hi,

I dont understand why yiu are importing the childwindows in the imodule implementation. And also why are you importing the sharedformmodule? Just to access the childwindow properties?

Move those 2 childwindow properties in your shell. And on module download completed those properties should be available. You dont need to import the sharedformsmodule at all.

On 2 Dec 2012 07:39, "highdownts" <notifications@codeplex.com> wrote:

From: highdownts

Hello,

I have created a small test solution to debug the Prism/Child Window code. I believe the MEFBootstrapper is working properly. The Shell code executes to the LoadModuleCompleted event shown in the code below. You mentioned that the only way to use the exported child windows is to import them. Are you saying that I need to add a hardcoded dependency to import either of the two child windows? Is there a way to use Prism to access the module and then its two properties without adding a dependency? I tried adding a library reference to the SharedForms project and importing (one at a time) the SharedFormsModule and then the two child window properties, but this does not seem to work. Can you please explain how to import the two child windows?

For example, I get an exception using the following syntax for the module or its properties.

[Import(typeof(SharedFormsModule))]

public SharedFormsModule SharedFormsModule;

[Import(typeof(IModule))]

public IModule errowWindow;

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow errowWindow;

This is the Shell code.

[ImportingConstructor]

public Shell(IModuleManager ModuleManager, IModuleCatalog moduleCatalog)

{

InitializeComponent();

this.ModuleManager = ModuleManager;

this.moduleCatalog = moduleCatalog;

this.Loaded += (s, ev) =>

{

var module = moduleCatalog.Modules.Single(m => m.ModuleName.Equals("SharedFormsModule"));

if (module != null && module.State == ModuleState.NotStarted)

ModuleManager.LoadModule("SharedFormsModule");

ModuleManager.LoadModuleCompleted += (sn, evt) =>

{

MessageBox.Show("Module Loaded...");

// ... How to access either of the two child windows???

};

};

}

This is the child window project.

namespace SharedForms

{

[Export(typeof(SharedFormsModule))]

public class SharedFormsModule: IModule, ISharedFormsModule

{

public void Initialize()

{

}

[Import("SMMMessageWindow", typeof(ChildWindow))]

public ChildWindow SMMMessageWindow { get; set; }

[Import("ErrorWindow", typeof(ChildWindow))]

public ChildWindow ErrorWindow { get; set; }

}

}

Read the full discussion online.

To add a post to this discussion, reply to this email (CompositeWPF@discussions.codeplex.com)

To start a new discussion for this project, email CompositeWPF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Dec 2, 2012 at 1:25 PM

Hello gan_s,

Let me summarize my main objectives. First, I want to loosely couple the Shell through Prism to a separate XAP file that contains a Child Window. This child window would in turn load additional Views/View Models for selected tab views based upon user choices using Prism modules. That is, modules which download other modules, as they are needed. Right now, this lumped functionality is adding substantially to the size of the XAP file. In addition, a reasonable number of users will not use this functionality on a regular basis, so it is being downloaded unnecessarily.

Let me explain my misunderstanding.

When you said that the child window had to be imported, I thought you meant using Prism/MEF to accomplish this through some dedicated Prism functionality, for example, getting a reference to the module object so that I can then access its members. Apparently, this is not the case.

What I thought I could do is to encapsulate all of the functionality in a separate XAP file that would be downloaded as a module on demand. This would reduce the size of the initial XAP and allow smaller XAPs to be downloaded to give the user the exact functionality that they need. I did not want to include a project reference to the child window project in the main shell application. Are you saying that this is the only way to import the child window functionality?

If I need a project reference to the child window project in the Shell application, then I do not see that Prism/MEF is doing anything to reduce the initial download size. Is it not possible to achieve my objectives with the child window using prism?

Thanks for your patience and assistance.

Warren

Dec 2, 2012 at 4:00 PM

Hi Warren,

The idea behind loosely coupled app is to avoid having references, which prism/mef combination solves more easily.

For your case this what you do
- shell will download your sharedformsmodule which has 2 childwindows which are defined and exported in that module
- shell needs to open up the childwindow by calling show on them once they are available, i.e. When module is downloaded

For shell to access the childwindow you need to
- have 2 properties each for your childwindows in shell itself.
- no need to have a property that references the module you downloaded.

In module download completed you will check to see if your childwindows are imported. If yes call Show() on them. Its that simple. Unfortunately i'm travelling and not able to send you a working sample for your scenario. This is a very trivial problem. Let me kno if what i'm suggesting is making sense to you!

Cheers!

Dec 4, 2012 at 2:24 PM

Hello gan_s,

I understand that Prism/MEF is designed to loosely couple objects so that you do not have to have a reference. What I do not understand is the syntax for importing the child windows after downloading. The child window classes are named. Those names are buried in the module project that contains the code. I understand the cocept in your previous post, but I do not understand how to grab/import childwindows without a handle of some sort. Can you provide a simple example or explain how to do this.

Thanks, Warren

Dec 4, 2012 at 2:51 PM

Hi Warren,

I know its very confusing. Here is what I mean. The handle you are looking for is the contract name of your exports. And as you can see we are not forcing the shell to know the concrete type of your childwindow, by which I mean any other special interface local to the childwindow module. All you need to know is the export contract.

[ImportingConstructor]
public Shell(IModuleManager ModuleManager, IModuleCatalog moduleCatalog)
{
  InitializeComponent(); 
  this.ModuleManager = ModuleManager;
  this.moduleCatalog = moduleCatalog;
  this.Loaded += (s, ev) =>
 {
   var module = moduleCatalog.Modules.Single(m =>  m.ModuleName.Equals("SharedFormsModule"));
   if (module != null && module.State == ModuleState.NotStarted)
   ModuleManager.LoadModule("SharedFormsModule");
   ModuleManager.LoadModuleCompleted += (sn, evt) =>
   {
      MessageBox.Show("Module Loaded...");
      // At this point your childwinodw properties will be populated
      if (ErrorWindow != null && evt.Error != null) ErrorWindow.Show();

      if (SMMMessageWindow != null) SMMMessageWindow.Show();
   };
 };
}

[Import("SMMMessageWindow")]
public ChildWindow SMMMessageWindow { get; set; }

[Import("ErrorWindow")]
public ChildWindow ErrorWindow { get; set; }

Your module would look like this

namespace SharedForms
{
   [Export(typeof(SharedFormsModule))]
   public class SharedFormsModule: IModule, ISharedFormsModule
   {
      public void Initialize()
      {
      }
   }
}

Try this out and let me know.

Cheers!

Dec 4, 2012 at 2:51 PM

Hi Warren,

I know its very confusing. Here is what I mean. The handle you are looking for is the contract name of your exports. And as you can see we are not forcing the shell to know the concrete type of your childwindow, by which I mean any other special interface local to the childwindow module. All you need to know is the export contract.

[ImportingConstructor]
public Shell(IModuleManager ModuleManager, IModuleCatalog moduleCatalog)
{
  InitializeComponent(); 
  this.ModuleManager = ModuleManager;
  this.moduleCatalog = moduleCatalog;
  this.Loaded += (s, ev) =>
 {
   var module = moduleCatalog.Modules.Single(m =>  m.ModuleName.Equals("SharedFormsModule"));
   if (module != null && module.State == ModuleState.NotStarted)
   ModuleManager.LoadModule("SharedFormsModule");
   ModuleManager.LoadModuleCompleted += (sn, evt) =>
   {
      MessageBox.Show("Module Loaded...");
      // At this point your childwinodw properties will be populated
      if (ErrorWindow != null && evt.Error != null) ErrorWindow.Show();

      if (SMMMessageWindow != null) SMMMessageWindow.Show();
   };
 };
}

[Import("SMMMessageWindow")]
public ChildWindow SMMMessageWindow { get; set; }

[Import("ErrorWindow")]
public ChildWindow ErrorWindow { get; set; }

Your module would look like this

namespace SharedForms
{
   [Export(typeof(SharedFormsModule))]
   public class SharedFormsModule: IModule, ISharedFormsModule
   {
      public void Initialize()
      {
      }
   }
}

Try this out and let me know.

Cheers!

Dec 4, 2012 at 4:52 PM

Hello gan_s,

I did as you recommended, and the result was:

ex = {System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "MainApp.Shell") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "M...

I tried with only one child import and the result was the same.

I do not understand why this approach should work. If the module is not loaded at the application start through the bootstrapper, how does the shell know what to import?

Second, if the module is loaded, the class is essentially empty except for the initialization method. How does loading the module, which does not seem to have any knowledge of the two child classes, pass the exported child window classes when it is loaded in the Shell.

There appears to be some 'Black Magic' going on that I do not understand. It may be very simple as you said, but I still do not get it.

Thanks, Warren

Developer
Dec 4, 2012 at 7:22 PM
Edited Dec 4, 2012 at 7:24 PM

Hi Warren,

Based on my understanding in order to avoid having a reference to the child window's project in the main project, you will have to export your child windows instances using a common interface defined in a common project. This interface for example could define a Show method (and any other required method in order to interact with your ChildWindows as needed when importing them through this interface.

Also, if you are importing this child windows for example in your shell's view model, you should have to use your [Import] attributes with AllowDefault=true and AllowRecomposition=true (e.g. like this [Import("myChildWindow1", AllowDefault = true, AllowRecomposition = true)], as this will allow you to change the value of the imported property after the initial composition, which will be required if you are loading the modules containing the exported classes on demand.

I created a small sample portraying this scenario, you can find it in my Skydrive account under the name "OnDemandChildWindowSample".

I hope you find this useful,

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

Dec 4, 2012 at 8:15 PM

Hello Agustin,

Using an interface as the import vessel makes sense. That is what I had set up last week, but got side tracked with some of the other inputs above.

Your example made sense too, but unfortuneately in did not work. I had to add the Prism/MEF reference libraries to your projects, but other than that eveything compiled and ran. After clicking on the Click to Show button, the program ran, but the IChildWindow MyChildWindowProperty is NULL. I added a get/set variable so that I could view the Import value as it was being set, which confirmed that it is always null.

I am not sure why it does not work, because the logic appeared to be sound.

Any thoughts?

Thanks, Warren

Dec 4, 2012 at 8:47 PM

Hello again,

I just thought I would let you know that I am running Windows 7/32 bit/Silverlight 5/Prism 4 libs (11/11/2010) with Visual Studio 2010 Ultimate in case there is something in my system that is causing the difference between your results and mine.

Thanks for your help, Warren

Dec 5, 2012 at 6:23 AM
Edited Dec 5, 2012 at 6:31 AM

To make myself clear I never suggested adding a reference to the childwindow module (your SharedFormsModule). I suggested keeping it loose by exporting a typeof(ChildWindow) as ChildWindow is part of the sl framework itself. You can do it using interfaces, but that needs to be shared between your module and the shell, which you dont have to worry about in case of ChildWindow. I have done this in my app and it works absolutely fine.

As above you will just need to add the AllowDefault=true and AllowRecomposition=true for it to get repopulated once your module gets downloaded. To check you could even implement the IPartImportsSatisfied in your shell to check if the OnImportsSatisfied gets called once your module is downloaded and check if your childwindow properties are populated.

 

[Import("SMMMessageWindow", AllowDefault=true, AllowRecomposition=true)]
public ChildWindow SMMMessageWindow { get; set; }

[Import("ErrorWindow", AllowDefault=true, AllowRecomposition=true)]
public ChildWindow ErrorWindow { get; set; }

 

I would still not go the interface way as that's an additional abstraction and it really isn't doing much in your case other than just calling Show() on your childwindow. Too much of abstraction is not needed in your case. 

Developer
Dec 5, 2012 at 6:16 PM

Hi Warren,

I was able to run Agustin's sample with the same system setup as yours, except for one difference: I used the 4.1 version of Prism instead of version 4. As the sample is created with Prism 4.1, this could be the cause behind why you needed to re-add the references to the Prism libraries.

As far as I know, Prism 4 targets Silverlight 4 and Prism 4.1 targets Silverlight 5. However, in the description of your system setup you said that you are using Silverlight 5 with Prism 4 and this could be causing unexpected behaviors in your application.

Please, try again using the Prism 4.1 libraries for Silverlight 5 and let us know if this solves the problem. Alternative you could change the target version of the projects of the sample to Silverlight 4 to use Prism 4.

You can find Prism 4.1's assemblies in the following link:

As a side note, if you wish to register Prism 4.1's assemblies in visual studio, take into account that there is a known bug in the script used to register the assemblies. You can find more information about it an how to solve it in the following work item:

Regards,

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

Dec 5, 2012 at 6:39 PM

Hello Damian,

Thanks for your assistance. I was able to finally get Agustin's sample to work. It is likely that I grabbed the wrong version library files.

Regards,

Warren

Dec 5, 2012 at 7:11 PM
gan_s wrote:

To make myself clear I never suggested adding a reference to the childwindow module (your SharedFormsModule). I suggested keeping it loose by exporting a typeof(ChildWindow) as ChildWindow is part of the sl framework itself. You can do it using interfaces, but that needs to be shared between your module and the shell, which you dont have to worry about in case of ChildWindow. I have done this in my app and it works absolutely fine.

As above you will just need to add the AllowDefault=true and AllowRecomposition=true for it to get repopulated once your module gets downloaded. To check you could even implement the IPartImportsSatisfied in your shell to check if the OnImportsSatisfied gets called once your module is downloaded and check if your childwindow properties are populated.

 

[Import("SMMMessageWindow", AllowDefault=true, AllowRecomposition=true)]
public ChildWindow SMMMessageWindow { get; set; }

[Import("ErrorWindow", AllowDefault=true, AllowRecomposition=true)]
public ChildWindow ErrorWindow { get; set; }

 

I would still not go the interface way as that's an additional abstraction and it really isn't doing much in your case other than just calling Show() on your childwindow. Too much of abstraction is not needed in your case. 

 

Hello gan_s,

Thanks for your help. I did not mean to imply that you said to use a reference to the child window. The confusion has been on my end. I see the merits in both approaches that have been suggested and can see applications of both in my current project. That is why I working both approaches to completion.

One thing that had confused me was that you had recommended using the following in an earlier email.

[Import("SMMMessageWindow")]
public ChildWindow SMMMessageWindow { get; set; }

As you pointed out in your last email, by setting the AllowDefault and AllowRecomposition true the exception that I was experiencing went away. This make a lot of sense now. The application does not know anything about the import until after the module is loaded. Once loaded, the recompositon can occur and the import resolved.

I did the following to get your approach to work.

1. Set AllowDefault = true, must be set true for OnDemand loading to allow a value of null or its default value when the property is first imported.
2. Set AllowRecomposition = true, must be set true to accommodate the change in the property after the module is loaded.
3. I had to set evt.Error == null, rather than if (ErrorWindow != null && evt.Error != null) as you mentioned in your email.

I now have both approaches working thanks to you, Agustin, and Damian. Sorry it took me so long to absorb the basics.

There may still be a problem with the approach based upon some of my other requirements, at least for some code modules.

  1. First, from my research, it appears that these techniques rely on a parameterless constructor. From what I have read, there is no provision for importing a class with multiple constructors using MEF or passing parameter to the imported class. Are these assumptions correct?
  2. Second, I assume that these two approaches cannot handle a static class since it cannot derive from an interface and may not be derived from a public class (like ChildWindow). Is this assumption correct?
  3. Third, I assume that an interface is required if you want to actually use any public properties or methods? Is this assumption correct?
  4. Lastly, is there any supporting MS technologies that allow multiple constructors and parameters to be passed through Prism modules.

Thanks again for all the help...

Warren

Developer
Dec 6, 2012 at 5:22 PM

Hi Warren,

I'm glad you could make the approaches work, regarding your concerns:

  1. Take into account that when using dependency injection containers like MEF you can also use the constructor injection approach. This way you can specify imports through constructor parameters by adding the [System.ComponentModel.Composition.ImportingConstructorAttribute] attribute to your constructor. As far as I know, in cases where your class is defined with multiple constructors MEF will use the one where you put this attribute.
    For more information about this you could check the Declaring Imports section of the MEF Programming Guide.
  2. Based on my understanding, you won't be able to export a Static class to a container like MEF as this is designed to create and initialize instances of classes for you and keep a collection of these instances, which may not be possible for Static classes. On the other, by default MEF threats their Imports / Exports as Singletons instances, in which case, I believe the use of static classes could be replaced by the use of these Singleton instances.
  3. Regarding the use of interfaces to export your classes, as seen before this might not always be necessary. But it will be required when you need loosely coupling between your components, as this will allow you to have an importer to be completely decoupled from the specific implementation of the exported type.
  4. As far as I know, another dependency injection container that supports the constructor injection approach is Unity, which in cases where a target class contains multiple constructors,  this container will use the one that has the InjectionConstructor attribute applied to. Also, if there is more than one constructor, and none carries the InjectionConstructor attribute, Unity will use the constructor with the most parameters. For more information on this subject, you could check the Annotating Objects for Constructor Injection section of the Unity documentation.

I hope you find this handy,

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