Why Silverlight 3 Navigation cannot be fully leveraged when loading modules remotely with Prism 2

Now that the Prism team has shipped a new version of the guidance, I thought it would be a good time to blog on this subject, based on some research we did with Ezequiel, Matias and Julian. This article will not focus on how to workaround the limitations of using Silverlight 3 Navigation with Prism (as the guys who wrote the posts mentioned below have done a good job on that) instead it will focus on the why of these limitations:

Warning: Technical explanation coming below.

The Silverlight team is aware of the limitations mentioned below, so they might be taken into account for a future version.

Issue Description

Pages with code behind, from remotely loaded modules cannot be navigated to, calling the Frame's Navigate method. This prevents having functional views since they neither have code behind nor a ViewModel attached.

Cause of Issue

Note: To be able to perform the research below, add the following line in the LoadAssemblyFromStream method placed in the XapModuleTypeLoader class from the CAL:
Deployment.Current.Parts.Add(assemblyPart);

The Silverlight 3 Navigation framework goes over every AssemblyPart in the Deployment.Current.Parts collection if the Page to navigate to has code behind, as shown in the code below (PageResourceContentLoader class reflected code in the System.Windows.Navigation namespace from the System.Windows.Controls.Navigation assembly):

private static Type GetTypeFromAnyLoadedAssembly(string typeName)
        {
            Type type = null;
            foreach (AssemblyPart part in Deployment.Current.Parts)
            {
                /*THE LINE BELOW RETURNS NULL FOR REMOTELY LOADED ASSEMBLIES, BECAUSE THEY ARE NOT IN THE APPLICATION'S XAP*/
                StreamResourceInfo resourceStream = Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
                if (resourceStream != null)
                {
                    Assembly assembly = new AssemblyPart().Load(resourceStream.Stream);
                    if (assembly != null)
                    {
                        type = Type.GetType(typeName + "," + assembly, false);
                    }
                }
                if (type != null)
                {
                    return type;
                }
            }
            return type;
        }

As the above code shows, even if the AssemblyPart is in the collection it is not found by the Application.GetReourceStream method. From the method's MSDN site, the cause is:

"The GetResourceStream method allows you to load an arbitrary resource file from one of the following locations:

  • Embedded in the application assembly in the application package.
  • Embedded in a library assembly that is included in the application package.
  • Included in the application package."

So only assemblies in the application's original .xap file are found and not for other modules.

I hope the above explanation was useful.