Modularity QuickStarts for Silverlight
The QuickStarts included in this topic are the following:
Defining Modules in Code QuickStart. This QuickStart demonstrates how to define modules in the module catalog from code. This is the most powerful approach when defining modules in the module catalog, because you can create the catalog by combining the different methods of populating the catalog. Using this approach, you can use conditional logic to determine which module should be loaded.
Note
This QuickStart is available in both Silverlight and Windows Presentation Foundation (WPF) versions.
Remote Modularity QuickStart. This QuickStart demonstrates two concepts:
- It demonstrates how to asynchronously download modules from the Web using Silverlight XAP files. It also demonstrates how to partition your application into several smaller downloads, how to configure asynchronously downloading these modules on a background thread, and how to trigger downloading a module on demand.
- It demonstrates how modules can be declared in the modules catalog from a XAML file. Declaratively populating the catalog from XAML is very similar to populating the catalog from code. In this QuickStart, modules that share the same properties are gathered inside modules groups.
Scenarios
This section describes the scenarios included in the QuickStart.
Defining Modules in Code QuickStart
The QuickStart is composed of four modules: ModuleA, ModuleB, ModuleC, and ModuleD. These modules are directly referenced by the shell and added to the ModuleCatalog through code.
Modules can have dependencies between them. In the QuickStarts, ModuleA depends on ModuleD, and ModuleD depends on ModuleB. Therefore, ModuleD needs to be initialized before ModuleA, and ModuleB needs to be initialized before ModuleD.
A module can also be loaded and initialized on demand. This means that the module does not need to be loaded and initialized when the application starts; instead, it can be loaded and initialized when a particular event during the application life occurs. In the Defining Modules in Code QuickStart, ModuleC is loaded and initialized on demand when the user clicks a button in a view exposed by ModuleB.
Figure 1 illustrates the main window of the Defining Modules in Code QuickStart.
Figure 1
Defining Modules in Code QuickStart user interface — Silverlight version
Remote Modularity QuickStart
The Remote Modularity QuickStart contains four modules: ModuleW, ModuleX, ModuleY, and ModuleZ. The ModuleCatalog is declaratively created and populated with ModuleInfo instances in XAML from the ModulesCatalog.xaml file.
For this QuickStart, the modules are partitioned into three separate downloads:
- ModulesWY.xap. This module contains ModuleW and ModuleY; it will be asynchronously downloaded in the background. This is defined by the setting InitializationMode = "WhenAvailable". Because the ModuleManager detects that the modules in this group need to be downloaded, it will start downloading these modules after the application starts. As soon as the modules become available (when the download completes), they will be initialized. This technique can greatly decrease the time it needs to start an application, thereby improving the user experience.
- ModulesZ.xap. This module contains ModuleZ; it will also be asynchronously downloaded in the background because InitializationMode "WhenAvailable" is the default setting.
- ModuleX.xap. This module contains ModuleX. This module is downloaded only when the user clicks the Load module X button. The download will still occur asynchronously on a background thread. As soon as the module is downloaded, it will be initialized.
Modules within the same group can have dependencies between them. In the QuickStarts, ModuleY depends on ModuleW. Therefore, ModuleW needs to be loaded before ModuleY.
Note
Creating and populating a ModuleCatalog in XAML and downloading XAP files are two different concepts that can be used independently of each other. You can create and populate your ModuleCatalog, even if you do not download your modules. And it is possible to add a ModuleInfo from code, instead of XAML, that indicates a module should be downloaded in a XAP file.
The QuickStart implements a download handler in the RemoteModuleLoading.Silverlight.Web Project to simulate the latency when downloading the group that contains ModuleY over a network.
Figure 2 shows the main window of the Remote Modularity QuickStart after the modules were loaded.
Figure 2
Remote Modularity QuickStart user interface
Building and Running the QuickStarts
The QuickStarts ship as source code—this means you must compile them before you run them. These QuickStarts do not have any prerequisites.
To build and run the Defining Modules in Code QuickStart
- In Visual Studio, open the solution file Quickstarts\Modularity\DefiningModulesInCodeQuickstart\DefiningModulesInCode.sln.
- Set the desired version of the QuickStart as the startup project:
- To run the Silverlight version, right-click the DefiningModulesInCode.Silverlight project, and then click Set as StartUp Project.
- To run the desktop version, right-click the DefiningModulesInCode.Desktop project, and then click Set as StartUp Project.
- On the Build menu, click Rebuild Solution.
- Press F5 to run the QuickStart.
To build and run the Remote Modularity QuickStart
- In Visual Studio, open the solution file Quickstarts\Modularity\RemoteModularityQuickstart\RemoteModuleLoading.sln.
- Right-click the RemoteModuleLoading.Silverlight.Web project, and then click Set as StartUp Project.
- On the Build menu, click Rebuild Solution.
- Press F5 to run the QuickStart.
Note
It is not possible to start the RemoteModuleLoading.Silverlight project directly, without using the RemoteModuleLoading.Silverlight.Web project. Normally, you can start Silverlight applications directly, without using a Web application project. The Silverlight Application Project will then dynamically create a test page that will host the application. However, the Silverlight security model does not allow the downloading of modules when the application is hosted in a dynamically created test page.
Walkthrough
Perform the following steps in any of the Modularity QuickStarts to explore the scenario.
To explore the scenario
- Open one of the provided solution files:
- Quickstarts\Modularity\DefiningModulesInCodeQuickstart\DefiningModulesInCode.sln. This is the solution file for the Defining Modules in Code QuickStart.
- Quickstarts\Modularity\RemoteModularityQuickstart\RemoteModuleLoading.sln. This is the solution file for the Remote Modularity QuickStart.
- On the Build menu, click Rebuild Solution.
- Press F5 to run the application.
The following sections are specific for each version of the Modularity QuickStarts.
Defining Modules in Code QuickStart
The main window shows a stack of views, each of which is loaded by a different module, as illustrated in Figure 3. Note that ModuleB's view includes a button to load the ModuleC.
Figure 3
Defining Modules in Code QuickStart main window
The order in which views appear in the window reflects the modules load order. The load order is determined by the dependencies between modules.
Note
If no dependencies are specified, the module load order is non-deterministic.
Click the Load Module C button to load ModuleC. When ModuleC loads, it adds a view to the window, as shown in Figure 4.
Figure 4
When the Load Module C button is clicked, ModuleC gets loaded
Remote Modularity QuickStart
The main window shows a stack of views, each of which is loaded by a different module. Modules can reside within a module group or not. Note that ModuleY's view includes a button to load the ModuleX.
The order in which views appear in the window reflects the modules initialization order. The initialization order is non-deterministic, but it satisfies the dependencies between modules. This ensures that modules with dependencies will be initialized after the modules they depend on are initialized. Figure 5 shows the QuickStart main window.
Figure 5
Remote Modularity QuickStart main window
The QuickStart implements a delay download manager to simulate network latency when downloading Module Y.
Click the Load Module X button to load ModuleX. When ModuleX loads, it adds a view to the window, as shown in Figure 6.
Figure 6
When the Load Module X button is clicked, ModuleX gets loaded
Implementation Details
The QuickStarts highlight the key components in modularity. The following sections describe the key artifacts of each QuickStart.
Defining Modules in Code QuickStart
The Defining Modules in Code QuickStart demonstrates how modules are defined in code in the module catalog. This is the most straightforward method of populating the module catalog. In this approach, the module information is added to the module catalog in code. If you have modules referenced directly, you can use the module type to add the module. However, directly referencing modules results in a less decoupled design. If you do not directly reference modules from the shell, you must provide the fully qualified type name, and the location of the assembly.
Another advantage of this approach is that you can easily add conditional logic to determine which modules should be loaded.
Module Catalog Setup
The ModuleCatalog is responsible of having the metadata for modules and modules groups in the application. The ModuleCatalog class allows you to programmatically register modules groups and modules.
To set up the module catalog, the GetModuleCatalog template method has to be overridden to return a valid instance of a catalog.
On the ModuleCatalog instance, modules have to be registered by invoking the AddModule method on it. The AddModule method takes the following parameters:
- The module initializer class type. A module initializer class is a class that implements the IModule interface.
- The names of the modules that the module depends on.
- The initialization mode, which specifies at which stage the module will be initialized. This parameter can take the following values:
- InitializationMode.WhenAvailable. This is the value for modules that will be initialized when they are available, this could also be when the application starts.
- InitializationMode.OnDemand. This is the value for modules that will be initialized when explicitly requested.
The following code shows how four modules are added to the ModuleCatalog instance that will be returned in the GetModuleCatalog template method.
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
// There are two ways of adding modules to the catalog in code
// 1: If the shell has a direct reference to the modules, you can use
// typeof(Module) to add the module.
catalog.AddModule(typeof (ModuleA), "ModuleD")
.AddModule(typeof (ModuleD), "ModuleB")
.AddModule(typeof (ModuleC), InitializationMode.OnDemand)
;
// 2: If the shell does not have a direct reference to the module,
// you have to specify the Assembly qualified typename.
// You might also have to specify where the file can be found
// by specifying a 'Ref' value. (In this example it's not needed,
// because the assembly is in the .net probe path)
// Define the name of ModuleB
const string moduleBAssemblyQualifiedName = "Modules.ModuleB.ModuleB, Modules, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
catalog.AddModule(new ModuleInfo("ModuleB", moduleBAssemblyQualifiedName));
return catalog;
}
Loading and Initializing Modules on Demand
This QuickStart demonstrates how to load a module on demand. A module is loaded on demand in reaction to an event during the application life cycle, instead of being loaded on application startup. In this QuickStart, the module ModuleC is configured to be loaded on demand.
The following code shows the registration of ModuleC within the ModuleCatalog class. In this case, the InitializationMode parameter has been set to InitializationMode.OnDemand. This indicates that the module should not be loaded when the module is available.
catalog.AddModule(typeof (ModuleC), InitializationMode.OnDemand)
The following code shows the DefaultViewB view implementation (located at ModuleB\DefaultViewB.xaml.cs), which exposes a button to load the ModuleC module on demand. The OnLoadModuleCClick method in the following code is the event handler for the click event of the button; it is in charge of loading the ModuleC module.
public partial class DefaultViewB : UserControl
{
private readonly IModuleManager moduleManager;
public DefaultViewB()
{
this.InitializeComponent();
}
public DefaultViewB(IModuleManager moduleManager)
: this()
{
this.moduleManager = moduleManager;
}
private void OnLoadModuleCClick(object sender, RoutedEventArgs e)
{
// This logic is placed in code-behind instead of a presenter
// for the ease of demonstrating module loading.
this.moduleManager.LoadModule("ModuleC");
}
}
As seen in the preceding code, a ModuleManager instance is obtained via constructor injection. This instance will be used to load and initialize the ModuleC module when requested.
Remote Modularity QuickStart
The Remote Modularity QuickStart leverages the usage of the modules catalog to retrieve and initialize modules that may reside on the main XAP file or at different locations. Remote module loading is useful for scenarios where there are some modules that are necessary for the application startup and other modules that are not. Downloading the modules that are not required for startup—on background threads after the required modules are downloaded—can greatly reduce the time needed to initiate the application.
Module Catalog
The ModuleCatalog is responsible for having the metadata for modules and modules groups in the application.
The module information can be defined in a XAML file. An advantage of doing this, compared to loading module information from a configuration file, is that loading from a XAML file instantiates the ModuleInfo objects.
To set up the module catalog, the GetModuleCatalog template method has to be overridden to return a valid instance of the catalog. The following code shows how to obtain and return a catalog instance from the ModulesCatalog.xaml XAML file.
protected override IModuleCatalog GetModuleCatalog()
{
return ModuleCatalogBuilder.CreateFromXaml(new Uri("/RemoteModuleLoading.Silverlight;component/ModulesCatalog.xaml", UriKind.Relative)).GetCatalog();
}
As seen in the preceding code, to obtain the catalog from a XAML file, you have to use the CreateFromXaml static method of the ModuleCatalogBuilder class, passing the XAML catalog file as a parameter, and then call the GetCatalog method to obtain the catalog instance that will be returned.
The Modules Catalog defined for the Remote Modularity QuickStart is shown in the following code:
<Modularity:ModuleCatalogBuilder xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Composite.Modularity;assembly=Microsoft.Practices.Composite">
<Modularity:ModuleInfoGroup Ref="ModuleX.Silverlight.xap" InitializationMode="OnDemand">
<Modularity:ModuleInfo ModuleName="ModuleX" ModuleType="ModuleX.ModuleX, ModuleX.Silverlight, Version=1.0.0.0" />
</Modularity:ModuleInfoGroup>
<Modularity:ModuleInfoGroup Ref="ModulesWY.Silverlight.xap" InitializationMode="WhenAvailable">
<Modularity:ModuleInfo ModuleName="ModuleY" ModuleType="ModuleY.ModuleY, ModulesWY.Silverlight, Version=1.0.0.0">
<Modularity:ModuleInfo.DependsOn>
<sys:String>ModuleW</sys:String>
</Modularity:ModuleInfo.DependsOn>
</Modularity:ModuleInfo>
<Modularity:ModuleInfo ModuleName="ModuleW" ModuleType="ModuleW.ModuleW, ModulesWY.Silverlight, Version=1.0.0.0">
</Modularity:ModuleInfo>
</Modularity:ModuleInfoGroup>
<!-- Module info without a group -->
<Modularity:ModuleInfo Ref="ModuleZ.Silverlight.xap" ModuleName="ModuleZ" ModuleType="ModuleZ.ModuleZ, ModuleZ.Silverlight, Version=1.0.0.0" />
</Modularity:ModuleCatalogBuilder>
As seen in the preceding code, modules can be declared within a Module Group or without one. The Remote Modularity QuickStart has two module groups; the first group contains a single module, and the other group contains two modules. Each group corresponds to a different XAP file, and each module is represented as a single ModuleInfo object.
The properties defined by the ModuleInfoGroup class are the following:
- Ref. The content of this property indicates the name of the XAP file where the modules of this group are contained.
- InitializationMode. This property specifies how all the modules inside the group are going to be initialized. This property can take the following values:
- WhenAvailable. The modules will be initialized as soon as they are available. If they are not available when the application starts, they are retrieved. The retrieval of modules is done asynchronously—on a background thread—to avoid freezing the user interface. This is the default initialization mode.
- OnDemand. The modules inside this group are downloaded and/or initialized only when explicitly requested.
Note
Dependencies among modules can be defined in the ModuleInfo objects. These dependencies can only be set among modules that reside in the same module group. For more information about defining dependencies, see How to: Define Dependencies Between Modules.
In the Remote Modularity QuickStart, ModuleY, ModuleW, and ModuleZ will be downloaded in the background and initialized when their retrieval is completed if they are not already available when the application starts, as indicated in the InitializationMode="WhenAvailable" property. On the other hand, ModuleX will be initialized on demand, that is, when explicitly requested.
Note
The ModuleCatalog.xaml file is not automatically generated; it should be manually created and added to the project as an existing item.
Module Manager
The ModuleManager class coordinates the initialization of the modules. It manages the retrieval and the subsequent initialization of the modules.
The Run method of the ModuleManager class, which is used to retrieve and initialize the modules described in the catalog, is shown in the following code.
public void Run()
{
this.moduleCatalog.ValidateCatalog();
this.LoadModulesWhenAvailable();
}
As seen in the preceding code, the strategy used by the ModuleManager is the following:
- It first validates the catalog. In this step, the following items are verified in the catalog:
- Each registered module is unique.
- There are no cyclic dependencies.
- A module inside a group does not have dependencies on modules in another group.
- A startup module does not depend on an on-demand module.
- It will load modules when available. In this step, the following things occurs:
A list of all modules is obtained.
To determine whether a module should be retrieved, its State property will be checked; if its state is NotStarted and needs retrieval (a module needs retrieval when its type cannot be resolved), it will iterate over the collection of available ModuleRetrievers, querying each one of them to determine if they can retrieve the modules.
The BeginRetrieval method will be invoked on the first ModuleRetriever capable of retrieving the module and its state will change to Retrieving.
Note
The Composite Application Library for WPF and Silverlight currently ships two retrievers or type loaders: the XapModuleTypeLoader, only available in Silverlight, which downloads Silverlight modules packed inside XAP files, and the FileModuleTypeLoader, only available in WPF, which retrieves modules from the file system. Retrievers are an important extensibility point; you can create and plug in your own retriever.
Modules that at this point are ready to be loaded—its State is ReadyforLoading—will be initialized. The manager will call the Initialize method of the ModuleInitializer class on each module that is available.
Loading Modules on Demand
A module is loaded on demand in reaction to an event during the application life cycle, instead of being loaded when the application starts.
The following code shows how a module group, ModuleX, is registered within the module catalog (ModuleCatalog.xaml file) for on-demand loading, by setting its InitializationMode property to OnDemand. This indicates that the module group should not be downloaded and/or initialized unless it is explicitly requested.
<Modularity:ModuleCatalogBuilder xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Composite.Modularity;assembly=Microsoft.Practices.Composite">
<Modularity:ModuleInfoGroup Ref="ModuleX.Silverlight.xap" InitializationMode="OnDemand">
<Modularity:ModuleInfo ModuleName="ModuleX" ModuleType="ModuleX.ModuleX, ModuleX.Silverlight, Version=1.0.0.0" />
</Modularity:ModuleInfoGroup>
<Modularity:ModuleInfoGroup Ref="ModulesWY.Silverlight.xap" InitializationMode="WhenAvailable">
<Modularity:ModuleInfo ModuleName="ModuleY" ModuleType="ModuleY.ModuleY, ModulesWY.Silverlight, Version=1.0.0.0">
<Modularity:ModuleInfo.DependsOn>
<sys:String>ModuleW</sys:String>
</Modularity:ModuleInfo.DependsOn>
</Modularity:ModuleInfo>
<Modularity:ModuleInfo ModuleName="ModuleW" ModuleType="ModuleW.ModuleW, ModulesWY.Silverlight, Version=1.0.0.0">
</Modularity:ModuleInfo>
</Modularity:ModuleInfoGroup>
<!-- Module info without a group -->
<Modularity:ModuleInfo Ref="ModuleZ.Silverlight.xap" ModuleName="ModuleZ" ModuleType="ModuleZ.ModuleZ, ModuleZ.Silverlight, Version=1.0.0.0" />
</Modularity:ModuleCatalogBuilder>
The following code shows the LoadModule_ButtonClick sample method, which loads the ModuleX module on demand. This method is an event handler of the click event of a button.
public partial class DefaultViewY
{
private readonly IModuleManager moduleManager;
public DefaultViewY()
{
InitializeComponent();
}
public DefaultViewY(IModuleManager moduleManager)
: this()
{
this.moduleManager = moduleManager;
}
private void LoadModule_ButtonClick(object sender, RoutedEventArgs e)
{
// This logic is placed in code-behind instead of a presenter
// for the ease of demonstrating module loading.
this.moduleManager.LoadModule("ModuleX");
}
}
As seen in the preceding code, to load a module on demand, you need to get a ModuleManager instance and invoke the LoadModule method on that instance, passing the module name as a parameter.
If the on-demand module needs retrieval, such as if the module group that contains it has not been downloaded yet, it will use the same retrieval strategy as the background modules.
Acceptance Tests
The Modularity QuickStarts for Silverlight include a separate solution that includes acceptance tests for both QuickStarts. Acceptance tests describe how an application should perform when you follow a series of steps; you can use the acceptance tests to explore the functional behavior of the applications in a variety of scenarios.
Some acceptance tests were developed using the testing framework White. To run these tests, you need to have White installed. For more information about White, including download information, see White on CodePlex.
Note
The acceptance tests have been developed and verified with the White 0.1.5.0 release. Although other releases of White might work, it is recommended to use this release to avoid any issues when running the tests.
To run the Modularity QuickStarts acceptance tests
- Place the assemblies required by White in the folder Source\Lib\White. The files are the following:
- Bricks.dll
- Bricks.RuntimeFramework.dll
- Castle.Core.dll
- Castle.DynamicProxy2.dll
- Core.dll
- log4net.config
- log4net.dll
- nunit.framework.dll
- White.NUnit.dll
- Xstream.Core.dll
- In Visual Studio, open the solution file Quickstarts\Modularity\Modularity.Tests.AcceptanceTest\Modularity.Tests.AcceptanceTest.sln.
- Right-click Modularity.Tests.AcceptanceTest, and then click Set as StartUp Project.
- Press F5.
Outcome
You should see the QuickStarts windows and the tests automatically interact with the user interface. At the end of the test pass, you should see that all tests have passed.
More Information
To learn about other QuickStarts included with the Composite Application Guidance, see the following topics: