Table of contents
- Introduction
- Initializing Prism Applications
- Manage dependencies between components
- Modular Application Development
- Implementation of the MVVM pattern
- Advanced MVVM scripts
- Creating user interface
- User Interface Design Recommendations
- Navigation
- View-Based Navigation (View-Based Navigation)
- The interaction between loosely coupled components
A modular application is such an application that can be divided into a number of functional blocks (modules) that can be integrated into one whole. The client module encapsulates part of the overall functionality of the application and is usually a set of interrelated functional parts. It may include a set of related components, such as application functions, including user interface and business logic, or parts of the application infrastructure, such as application-level services for logging, or authenticating and authorizing users. Modules are independent of each other, but can interact with each other in a weakly coupled way. Modular applications can facilitate the development, testing, deployment, and expansion of your application.
For example, consider a personal banking application. The user can access a variety of functions, such as transferring money between accounts, paying bills, and updating personal data using a single user interface (UI). However, each of these functions is encapsulated within a discrete module. These modules communicate with each other and with backend systems, such as database servers and web services. Application services integrate the various components within each of the various modules and handle user interaction. The user sees an integrated view that looks like a single seamless app.
The following illustration shows the modular application project.
')

Benefits from creating modular applications
You are probably already creating a well-designed application using assemblies, interfaces, classes, and good object-oriented design principles. In this case, if you do not take serious measures, the design of your applications will remain "monolithic" (where all the functionality is implemented in a highly connected way within the application), this can lead to the application being difficult to develop, test, expand, and maintain.
A modular approach to building an application, on the other hand, can help highlight large-scale functional areas and develop or test them independently. This can make development and testing easier, and your application more flexible and easily expandable in the future. The advantage of the modular approach is that it can make the application architecture more flexible and maintainable by dividing the application into parts that are easy to manage. Each part encapsulates a specific functionality, and integrates through clear, but weakly connected channels.
Prism development support for modular applications
Prism helps you develop modular applications and manage modules at runtime. Using the functionality of Prism, you can save time, since you do not need to implement and test your own platform for building modular applications. Prism supports the following functions for developing modular applications:
- Catalog of modules for registering named modules, or determining their location; You can create a catalog of modules in the following ways:
- Defining modules in code, or in XAML.
- For WPF: detecting modules in a directory, so you can load all modules without explicitly defining them in a centralized directory.
The note.
When using this approach, assemblies with modules may not be compiled automatically, when the project is built, and not copied into the output directory, since they are not referenced from the project of the executable file.
- For WPF: defining modules in the configuration file.
- Declaratively set attributes for modules, which are used to define the initialization mode and dependency injection.
- Integration with dependency injection containers to maintain loose coupling between modules.
- To load modules:
- Dependency management, including duplication and loop detection, to ensure that modules load in the correct order and that they are loaded and initialized only once.
- Background loading of modules and loading on demand to minimize the launch time of the application; the rest of the modules can be loaded and initialized in the background or as needed.
Basic concepts
This section provides basic concepts related to the modularity of Prism, including the
IModule
, the module loading process, the module catalog, the communication between the modules, and the dependency injection containers.
IModule : modular application building block
A module is a logical set of functional components and resources collected in one place. Modules can be developed, tested, deployed, and integrated into the application separately. A package can include one or more assemblies, which can be located separately, or assembled in a single XAP file. Each module has a central class that is responsible for initializing and integrating the functionality of the module into the application. This class implements the
IModule
. The presence of a class that implements the
IModule
is enough to identify the package as a module. The
IModule
has a single
Initialize
method, within which you can implement any logic necessary to initialize and integrate the functionality of the module into the application. Depending on the purpose of the module, it can register views in the regions of the user interface, make additional services available to the application, or extend its functionality. The following code shows the minimal implementation of the module.
public class MyModule : IModule { public void Initialize() {
The note
Instead of using the initialization mechanism provided by the IModule
, Stock Trader RI uses a declarative, attribute-based approach to register views, services, and types.
Module lifetime
The process of loading a module into Prism includes the following:
- Registration / detection of modules. The modules that will be loaded at runtime for a specific application are defined in the modules directory. The catalog contains information about the loadable modules, their location, and the order in which they should be loaded.
- Loading modules. Assemblies that contain modules are loaded into memory. This phase may require loading the module from the network or from another remote location or local directory.
- Initialization of modules. Creating a module class and calling the
Initialize
method via the IModule
.
The following illustration shows the module loading process.

Module Catalog
ModuleCatalog
contains information about the modules that can be used by the application. A directory is essentially a collection of
ModuleInfo
classes. Each module is described by the
ModuleInfo
class, which stores the name, type, and location of the module. There are several typical approaches to filling
ModuleCatalog
with
ModuleCatalog
instances:
- Registration of modules in the code.
- Register modules in XAML.
- Registration of modules in the configuration file (only in WPF).
- Discovery of modules in the local directory on disk (WPF only).
The choice of registration and detection mechanism to use depends on what the application needs. Using a configuration file or a XAML file allows an application not to store references to modules. Using a local directory may allow an application to detect modules without having to define them in any file.
Management of the module loading moment
Prism applications can initialize modules as soon as possible, what is known as “when available”, or when the application needs them, what is known as “on-demand”. For Silverlight applications, modules can be loaded with the application, or in the background after the application starts. Consider the following module loading instructions:
- The modules required for the application to work must be loaded with the application and initialized at startup.
- Modules containing functions that are almost always used during normal use of an application can be loaded in the background and initialized when they become available.
- Modules containing functions that are rarely used (or support modules on which other modules may depend) can be loaded in the background and initialized on demand.
Consider how the application is divided, common use scenarios, application launch rate, and the number and size of downloads to determine how to configure the loading and initialization of modules.
Module integration with the application
Prism provides the following classes needed to load your application:
UnityBootstrapper
and
MefBootstrapper
. These classes can be used to create and configure a module manager, which is required to detect and load modules. You can override the module directory configuration method to register the modules defined in the XAML file, the configuration file, or specify the directory location.
Use the module
Initialize
method to integrate the module with the rest of the application. The way you do this will depend on the structure of the application and the content of the module. Below are the general steps you need to take to integrate the module into your application:
- Add the views contained in the module to the navigation structure of the application. This is a common step in creating a composite application UI using detection, or injection of views.
- Sign up for application or service level events.
- Register shared services in the application dependency injection container.
Communication between modules
Even considering that modules should be weakly connected, they tend to exchange data and messages with each other. There are several communication patterns for loosely coupled systems, each with its own advantages and disadvantages. As a rule, eventually, combinations of these patterns are used. The following are some of these patterns:
- Weakly related events. A module can broadcast that a specific event has occurred. Other modules may subscribe to receive such events. Thus, they will be notified when this event occurs. Weakly coupled events are a fairly easy way to establish communication between modules. In this regard, it is easily implemented. However, a project that relies too much on events may become difficult to support, especially if there are many events, or they must be organized together to perform a single task. In this case, the use of a common service may be more rational.
- Shared services. A shared service is a class that can be accessed through a common interface, usually allowed through a container. As a rule, shared services are in a shared assembly and provide system-wide services, such as authentication, logging, or configuration. Such services are often singletons.
- Shared resources. If you do not want modules to communicate directly with each other, you can have them communicate indirectly through a shared resource, such as a database or a series of web services.
Dependency injection and modular applications
Containers such as the Unity Application Block (Unity) and the Managed Extensibility Framework (MEF) allow you to easily use Control Inversion (IoC) and dependency injection, which are powerful design patterns that help connect components in a loosely coupled way. This allows components to get references to other components on which they depend, without the need to hard code these links, thus obtaining code reuse and increased flexibility. Dependency injection is very useful when creating a loosely coupled modular application. Prism was designed to be independent of the DI container used to compose the components of an application. The choice of container is your business, and it will largely depend on the basic operational characteristics and preferences. There are two main dependency injection platforms for consideration from Microsoft - Unity and MEF.
Patterns & practices The Unity Application Block represents a full-featured dependency injection container. It supports property-based injection, constructor-based injection, and policy injection, which allows you to transparently introduce behaviors and policies between components. It also supports many other features typical of dependency injection containers.
MEF (which is now part of the .NET Framework 4 and Silverlight 4) allows you to create extensible applications, support dependency-based composition, and also provides other functions that support modular application development. This allows an application to detect components at runtime and then integrate them into the application. MEF provides excellent opportunities for expansion and composition. These include detecting assemblies and types, resolving dependencies, introducing dependencies, and some features for downloading assemblies and XAP files. Prism supports the use of MEF features such as:
- Association of module types with the location of their XAP files.
- Registration of modules through XAML and in code for WPF and Silverlight.
- Registration of modules through configuration files and scanned directories for WPF.
- Monitoring the loading status of modules.
- Custom declarative metadata for modules.
Key decisions
The first decision you have to make is whether you want to develop a modular application. There are numerous advantages to creating modular applications, as discussed in the previous section, but there is also additional time and effort that you must make to get these benefits. If you still decide to develop a modular application, then there are a few things to consider:
- Determine the platform you will use. You can create your own modular platform, use Prism, MEF, or another platform.
- Determine how to organize your application. Approach the modular architecture by defining the boundaries of each module, including which assemblies are part of each module. You can decide to use the modular principle to facilitate development, to control how the application is deployed, or to support a plug-in or extensible architecture.
- Determine how to split your modules. Modules can be divided based on requirements, for example, by functional areas provided by modules, development teams and deployment requirements.
- Identify the basic services that the application will provide to all modules. For example, it can be an error reporting service, or an authentication and authorization service.
- If you use Prism, determine how you will register the modules in the modules directory. For WPF, you can register modules in code, in XAML, in a configuration file, or configure module detection in a local directory on disk. For Silverlight, you can register modules in code or in XAML.
- Define your data transfer strategy and dependencies between modules. Modules will need to communicate with each other, and you will need to deal with dependencies between modules.
- Identify the dependency injection container. Modular systems typically require dependency injection, inversion of control, or a service locator to ensure loose coupling, dynamic loading, and the creation of modules. Prism allows you to choose between Unity, MEF, or another container and provides libraries for Unity or MEF based applications.
- Minimize the launch time of the application. Consider loading modules on demand and background loading to minimize application launch time.
- Determine deployment requirements. You will need to think about how you intend to deploy your application. This may affect the number of assemblies collected in XAP files. You might also share shared libraries, such as Prism, to use assemblies cached in Silverlight.
The following sections examine these decisions in detail.
Dividing an application into modules
When you develop your application in a modular way, you structure it into separate client modules that can be individually developed, tested, and deployed. Each module will encapsulate part of the full functionality of your application. One of the first design decisions that needs to be made is to decide how to divide the functionality of your application into discrete modules.
A module must encapsulate a number of related functionality and have a set of different responsibilities. A module can represent a vertical section of an application or a horizontal layer of services. Large applications are likely to have both types of modules.


A large application can have modules organized both in vertical sections and in horizontal layers. For example, modules may include the following:
- A module that contains a specific application function, such as the news module in the Stock Trader Reference Implementation (Stock Trader RI) .
- A module that contains a specific subsystem or functionality for a number of related usage sections, such as purchase, invoicing, or general ledger.
- A module that contains infrastructure services, such as logging, caching, and authorization services, or web services.
- A module that contains services that invoke line-of-business (LOB) systems, such as Siebel CRM and SAP, in addition to other internal systems.
A module should have a minimum set of dependencies on other modules. When a module has a dependency on another module, it must be loosely associated with it through the interfaces defined in the shared library, not through specific types, or, using
EventAggregator
, to communicate with other modules through events.
The goal of the modular principle is to divide the application in such a way that it becomes flexible, maintainable, and sustainable, even when functions and technologies are added and removed. The best way to achieve this is to develop your application so that the modules are as independent as possible, have well-defined interfaces, and are as isolated as possible.
Determining the ratio of projects and modules
There are several ways to create and package modules. The recommended and most common way is to create a single assembly per module. It helps to separate modules logically and promotes proper encapsulation. It also allows you to talk about the assembly as a module and vice versa. However, nothing prevents a single assembly from containing multiple modules, in some cases it may be preferable to minimize the number of projects in your solution. A large application may well have 10-50 modules. Moving each module to your own project adds complexity to the solution and can slow down Visual Studio performance. Sometimes it makes sense to divide a module or a set of modules into your own solutions if you want to stick with one module per project or assembly.
XAP and factoring module
For Silverlight applications, modules are usually packaged in separate XAP files, although in some cases, you may have more than one module per XAP. Consider how many XAP files you need to minimize the number and size of download requests required to run an application and activate a new option. If you want to split each module into its own project / assembly, you must decide whether to insert each assembly into its own XAP for deployment or include several assemblies in a single XAP.
Some factors affecting your choice of whether to include several modules in a single XAP file or to separate them:
- Download size and shared dependencies. Each XAP file has a small amount of additional size overhead in its declaration and .zip package. In addition, if there are common dependencies between modules, and they do not belong to a dependent module or a cached library, then each XAP will include these dependent libraries, which can significantly increase the download size.
- Synchronize when multiple modules are needed by the application. If several modules are loaded and used at the same time, such as providing views when the application is started, packing them in a single XAP file can make the download a little faster and guarantee that both modules will be physically available to the client at the same time. The function of the modular principle Prism ensures that modules that indicate dependencies are loaded in the correct order. But there is a small amount of performance overhead involved in creating two downloads instead of one, even if the total download size is the same.
- Version control modules. If different modules are developed on independent timelines and potentially deployed separately, you may want to put them in separate XAPs so that they can be marked with different versions more cleanly and updated independently.
To avoid loading the same builds several times in each XAP, there are two approaches that can be used:- Spread shared dependencies into a separate infrastructure module, and have consumable modules that take dependencies from this shared module.
- Use Assembly Library Caching in Silverlight to put shared types in a shared library that is loaded once and cached by Silverlight, not by the Prism module loader.
Use dependency injection to achieve loose coupling.
A module may depend on components and services provided by the host application or other modules. Prism supports the ability to register dependencies between modules so that they are loaded and initialized in the correct order. Prism also supports initialization of modules after they are loaded. During initialization, a module can get links to additional components and services that it needs, and / or register any components and services it contains to make them available to other modules.A module must use an independent mechanism to get instances of external interfaces instead of directly creating specific types. It can do this through a dependency injection container or factory services. Dependency injection containers, such as Unity or MEF, allow a type to automatically obtain interface instances through dependency injection. Prism integrates with both Unity and MEF to allow modules to easily use dependency injection.The following diagram shows a typical sequence of operations when loading modules that are required to get or register links to components and services.
In this example, the assembly OrdersModule
defines a classOrdersRepository
(along with other views and classes that implement the functionality of the order). The assembly CustomerModule
defines a class CustomersViewModel
that depends on the class OrdersRepository
based on the interface provided by the service. The launch of the application and the download process include the following steps:- Bootstrapper starts the module initialization process, and the module loader loads and initializes
OrdersModule
. - In the initialization stage
OrdersModule
, it registers OrdersRepository
in a container. - Loader module loads
CustomersModule
. The order of loading modules can be determined based on their metadata. CustomersModule
CustomerViewModel
, . CustomerViewModel
OrdersRepository
( ) . , , OrdersModule
. CustomerViewModel
OrderRepository
.
, , OrderRespository
( IOrderRepository
), , , , . , CustomersModule
OrdersModule
.
Notice that both modules have an implicit dependency on the dependency injection container. This dependency is introduced during the creation of the module in the loader.Basic scripts
This section describes common scenarios that you encounter when working with modules in your application. These scenarios include defining a module, registering and discovering modules, loading modules, initializing modules, determining module dependencies, loading modules on demand, loading remote modules in the background, and determining the status of the loading process. You can register modules in code, in XAML, in the application's configuration file, or by scanning the local directory.Module definition
A module is a logical set of functionality and resources that can be separately developed, tested, deployed, and integrated into an application. Each module has a central class that is responsible for initializing the module and integrating its functionality into the application. This class implements the interface IModule
as shown below. public class MyModule : IModule { public void Initialize() {
Note
Module names must be unique within the entire application.
The way in which you implement the method Initialize
will depend on the requirements of your application. The class type of the module, the initialization mode, and any dependencies of the module are specified in the module directory. For each module in the directory, the loader creates an instance of the module class and then calls the method Initialize
. Modules are processed in the order defined in the modules directory. The order of initialization at run time depends on when the modules are loaded, become available, and their dependencies are satisfied.Depending on the type of module directory that your application uses, module dependencies can be set either by declarative attributes directly in the module class, or within the module directory file. The following sections will provide more detailed information.Registration and detection of modules
The modules that an application can load are defined in the modules directory. Module Loader Prism uses the module directory to determine which modules are available for download, when to load them, and in what order they should be loaded.The module directory is a class that implements the interface IModuleCatalog
. The module catalog class is created by the loader class during application initialization. Prism provides various implementations of a catalog of modules from which you can choose the one you need. You can also populate a catalog of modules from another data source by calling a method AddModule
or inheriting a class from ModuleCatalog
to create a catalog of modules with specialized behavior.
, Prism Common Service Locator, , . , , , , , . - .
The most basic module directory is provided by the class ModuleCatalog
. You can use this directory to register modules in code by specifying the class type of the module. You can also specify the initialization mode and the module name. To register a module directly in a class ModuleCatalog
, call a method AddModule
in the class of Bootstrapper
your application. protected override void ConfigureModuleCatalog() { Type moduleCType = typeof(ModuleC); ModuleCatalog.AddModule( new ModuleInfo() { ModuleName = moduleCType.Name, ModuleType = moduleCType.AssemblyQualifiedName, }); }
In the previous example, modules are directly referenced by the shell, thus the types of classes of modules that can be used in the method call are defined AddModule
. That is why this example uses typeof(Module)
to add modules to a directory.Note
If the application has a direct link to the module type, you can add this type, as shown above. Otherwise, you must provide the fully qualified type name and location of the assembly.
To see another example of the determination module catalog in the code refer StockTraderRIBootstrapper.cs
to the Stock Trader the RI .Note
The base class Bootstrapper
provides a method CreateModuleCatalog
to assist in the creation ModuleCatalog
. By default, this method creates an instance ModuleCatalog
, but this method can be overridden in a derived class to create other types of module catalog.
Registering Modules Using the XAML File
You can define a catalog of modules declaratively in a XAML file. The XAML file determines which module directory class to create and which modules to add to it. Usually the .xaml
file is added as a resource to your shell project. The catalog of modules is created in the loader by a method call CreateFromXaml
. From a technical point of view, this approach is similar to the definition ModuleCatalog
in code, because the XAML file simply defines a hierarchy of objects to be instantiated.The following code example shows a XAML file that defines a directory of modules. <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 Ref="ModuleB.xap" InitializationMode="WhenAvailable"> <Modularity:ModuleInfo ModuleName="ModuleB" ModuleType="ModuleB.ModuleB, ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Modularity:ModuleInfoGroup> <Modularity:ModuleInfoGroup InitializationMode="OnDemand"> <Modularity:ModuleInfo Ref="ModuleE.xap" ModuleName="ModuleE" ModuleType="ModuleE.ModuleE, ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Modularity:ModuleInfo Ref="ModuleF.xap" ModuleName="ModuleF" ModuleType="ModuleF.ModuleF, ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" > <Modularity:ModuleInfo.DependsOn> <sys:String>ModuleE</sys:String> </Modularity:ModuleInfo.DependsOn> </Modularity:ModuleInfo> </Modularity:ModuleInfoGroup> <Modularity:ModuleInfo Ref="ModuleD.xap" ModuleName="ModuleD" ModuleType="ModuleD.ModuleD, ModuleD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Modularity:ModuleCatalog>
Notes
ModuleInfoGroups
provide a convenient way to group modules that are in the same .xap file or assembly, initialized in the same way, or have dependencies only on modules in the same group. The dependencies between the modules can be defined within the modules ModuleInfoGroup
, however, it is not possible to specify the dependencies between the modules in different ModuleInfoGroups
. Placing modules into groups of modules is optional. The properties that are set for the group will be applied to all modules in it. Note that modules can also be registered without being in a group.
In the class, Bootstrapper
you must indicate that the XAML file is the source for ModuleCatalog
, as shown below. protected override IModuleCatalog CreateModuleCatalog() { return ModuleCatalog.CreateFromXaml( new Uri("/MyProject.Silverlight;component/ModulesCatalog.xaml", UriKind.Relative)); }
Registration of modules using the configuration file
In WPF, it is possible to specify information about a module in a file App.config
. The advantage of this approach is that this file is not compiled into the application. This makes it easy to add or remove modules at runtime, without recompiling the application.The following code shows the configuration file that defines the module directory. If you want the module to automatically load, install startupLoaded="true"
. <modules> <module assemblyFile="ModularityWithUnity.Desktop.ModuleE.dll" moduleType="ModularityWithUnity.Desktop.ModuleE, ModularityWithUnity.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleE" startupLoaded="false" /> <module assemblyFile="ModularityWithUnity.Desktop.ModuleF.dll" moduleType="ModularityWithUnity.Desktop.ModuleF, ModularityWithUnity.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleF" startupLoaded="false"> <dependencies> <dependency moduleName="ModuleE"/> </dependencies> </module> </modules>
Note
Even if your assemblies are in the global assembly cache or in the same folder as the application, an attribute is required assemblyFile
. The attribute is used to map moduleType
to the correct IModuleTypeLoader
one to be used.
In the Bootstrapper
application class , you must specify that the configuration file is the source for ModuleCatalog
. To do this, use the class ConfigurationModuleCatalog
as shown in the following code. protected override IModuleCatalog CreateModuleCatalog() { return new ConfigurationModuleCatalog(); }
Note
You can still add modules to ConfigurationModuleCatalog
in the code. You can use this, for example, in order to make sure that the modules absolutely necessary for the operation of your application are added to the module directory.
Note
Silverlight does not support configuration files. If you want to use this approach for configuration in Silverlight, it is recommended to create your own ModuleCatalog
, which reads the configuration of modules from the web service on the server.
Locate modules in the local directory
The class DirectoryModuleCatalog
allows you to specify a local directory as a module directory in WPF. This module directory will scan the specified folder, and look for assemblies that provide modules for your application. To use this approach, you will need to use declarative attributes on your module classes to determine the module names and any dependencies they have. The following code example shows a directory of modules that is populated by detecting assemblies in a local directory. protected override IModuleCatalog CreateModuleCatalog() { return new DirectoryModuleCatalog() {ModulePath = @".\Modules"}; }
Note
This functionality is not supported in Silverlight, because the Silverlight security model does not allow you to load assemblies from the file system.
Module loading
Once ModuleCatalog
full, the modules are ready for loading and initialization. Loading a module means that the module assembly is loaded from disk into memory. If the assembly is not on disk, you may need to first get it from another source. An example of this is downloading an assembly from the Internet using Silverlight .xap
files. ModuleManager
responsible for coordinating the initialization and loading process.Module initialization
After loading the modules, they are initialized. This means that an instance of the module class is created and its method is Initialize
called. Initialization is where the module is integrated into the application. Consider the following options when initializing a module:- . , , .
- . / , .
Initialize
, . , , , . , . , MenuService
( ), Initialize
– , .
. , Initialize
, . , . , , , «» .
- . , Unity MEF, . , .
Determination of module dependencies
Modules may depend on other modules. If Module A depends on the Module B , the Module B must be initialized before Module A . ModuleManager
keeps track of these dependencies and initializes the modules in the correct order. Depending on how you defined your module catalog, you can define your module dependencies in code, configuration, or XAML.Determining code dependencies
For WPF applications that register modules in code or find them in a folder, Prism provides declarative attributes for the module class, as shown in the following example. [Module(ModuleName = "ModuleA")] [ModuleDependency("ModuleD")] public class ModuleA: IModule { ... }
Determination of dependencies in XAML
The following XAML shows that the Module F depends on the Module E . <Modularity:ModuleInfo Ref="ModuleF.xap" ModuleName="ModuleF" ModuleType="ModuleF.ModuleF, ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" > <Modularity:ModuleInfo.DependsOn> <sys:String>ModuleE</sys:String> </Modularity:ModuleInfo.DependsOn> </Modularity:ModuleInfo>
Determination of dependencies in the configuration file
The following example shows App.config file that Module D depends Module B . <modules> <module assemblyFile="Modules/ModuleD.dll" moduleType="ModuleD.ModuleD, ModuleD" moduleName="ModuleD"> <dependencies> <dependency moduleName="ModuleB"/> </dependencies> </module>
Loading modules on demand
To load modules on demand, you must specify that they should be loaded into the module directory with the parameter set InitializationMode
to OnDemand
. After that, you must add code to your application requesting module loading.Load on demand job in code
The definition that the module should be loaded on demand through attributes is shown in the following example. protected override void ConfigureModuleCatalog() { Type moduleCType = typeof(ModuleC); this.ModuleCatalog.AddModule(new ModuleInfo() { ModuleName = moduleCType.Name, ModuleType = moduleCType.AssemblyQualifiedName, InitializationMode = InitializationMode.OnDemand }); }
XAML load on demand job
You can determine InitializationMode.OnDemand
when you specify a module directory in XAML, as shown in the following code example. ... <Modularity:ModuleInfoGroup InitializationMode="OnDemand"> <Modularity:ModuleInfo Ref="ModuleE.xap" ModuleName="ModuleE" ModuleType="ModuleE.ModuleE, ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> ...
Set load on demand in the configuration file
You can specify InitializationMode.OnDemand
when specifying a directory of modules in a file App.config
, as shown in the following code example. ... <module assemblyFile="Modules/ModuleC.dll" moduleType="ModuleC.ModuleC, ModuleC" moduleName="ModuleC" startupLoaded="false"/> ....
Module load request
After the module is defined as loadable on demand, the application may request that it be loaded. The code that wants to initiate the download is required to obtain a link to the service IModuleManager
registered in the container by the loader. private void OnLoadModuleCClick(object sender, RoutedEventArgs e) { moduleManager.LoadModule("ModuleC"); }
Remote module loading in the background
Loading modules in the background after the application is launched, or only when the user needs them, can reduce the application launch time.Preparing the module for remote download
In Silverlight applications, modules are packaged into .xap
files. To load the module separately from the application, create a separate .xap
file. You may want to put several modules in a single .xap
file in order to optimize the number of load requests for replacing the size of each .xap file.Note
For each .xap file, you will need to create a new Silverlight application project. In Visual Studio 2008 and 2010, only application projects produce separate .xap
files. You will not need App.xaml
or files MainPage.xaml
in these projects.
Tracking the boot process
The class ModuleManager
provides an event to track the progress of loading modules. It provides loaded bytes versus full load size for percent promotion. You can use it to display visual indicators of the download progress. this.moduleManager.ModuleDownloadProgressChanged += this.ModuleManager_ModuleDownloadProgressChanged;
void ModuleManager_ModuleDownloadProgressChanged(object sender, ModuleDownloadProgressChangedEventArgs e) { ... }
Determining if a module has been loaded
The service ModuleManager
provides an event to track when a module is loaded or not able to load. this.moduleManager.LoadModuleCompleted += this.ModuleManager_LoadModuleCompleted;
void ModuleManager_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e) { ... }
To keep the application and modules loosely coupled, you should avoid using this event to integrate the module with the application. Use the module method instead Initialize
.LoadModuleCompletedEventArgs
contains a property IsErrorHandled
. If the module fails to load, and the application wants to prevent it from ModuleManager
registering an error and throwing an exception, you must set this property to true
.Note
After the module is loaded and initialized, the module assembly cannot be unloaded. The module instance reference will not be saved by Prism libraries, so an instance of the module class can be garbage collected after initialization has been completed.
Modules in MEF
This section only highlights the differences when using MEF as a dependency injection container.Note
When using MEF, MefBootstrapper
uses MefModuleManager
. It extends ModuleManager
and implements the interface IPartImportsSatisfiedNotification
to ensure that it is ModuleCatalog
updated when new types are imported by MEF.
Registering Modules in Code Using MEF
When using MEF, you can apply an attribute ModuleExport
to module classes so that MEF can automatically detect them. [ModuleExport(typeof(ModuleB))] public class ModuleB : IModule { ... }
You can also use MEF to locate and load modules using a class AssemblyCatalog
that can be used to locate all exported module classes in an assembly, and a class AggregateCatalog
that allows multiple directories to be combined into one logical directory. By default, the class MefBootstrapper
creates an instance AggregateCatalog
. You can override the method ConfigureAggregateCatalog
to register assemblies. protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog();
The Prism implementation MefModuleManager
synchronizes AggregateCatalog
MEF and Prism ModuleCatalog
, thus allowing Prism to detect modules added through ModuleCatalog
or AggregateCatalog
.Note
MEF is widely usedLazy, Value
.
Locate modules in a local directory using MEF
MEF provides a class DirectoryCatalog
that can be used to view a folder for assemblies containing modules (and other MEF types exported). In this case, you override the method ConfigureAggregateCatalog
to register the directory. This approach is only available in WPF.
To use this approach, you must first define the module names and their dependencies using an attribute ModuleExport
, as shown in the following example.
protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); DirectoryCatalog catalog = new DirectoryCatalog("DirectoryModules"); this.AggregateCatalog.Catalogs.Add(catalog); }
Defining code dependencies using MEF
WPF, ModuleExport
, .
[ModuleExport(typeof(ModuleA), DependsOnModuleNames = new string[] { "ModuleD" })] public class ModuleA : IModule { ... }
MEF , . MEF ModuleCatalog
, , ModuleCatalog
, XAML ( - ). ModuleCatalog
MEF, ModuleCatalog
, DependsOnModuleNames
. MEF ModuleCatalog
Silverlight, XAP.
MEF
MEF ModuleExport
, , InitializationMode
, , , .
[ModuleExport(typeof(ModuleC), InitializationMode = InitializationMode.OnDemand)] public class ModuleC : IModule { ... }
MEF
, Prism, MEF, MEF DeploymentCatalog
, .xap
.xap . MefXapModuleTypeLoader
DeploymentCatalog
AggregateCatalog
.
.xap
, . , , . Microsoft.Practices.Prism.MefExtensions.dll
.
, DLL 'Copy Local'=false
. .xap . .xap . , , , .xap , , .xap .
Additional Information
For more information about assembly caching, see "How to: Use Assembly Library Caching" on MSDN: http://msdn.microsoft.com/en-us/library/dd833069 ( VS.95 ) .aspx
To learn more about modular principle in Prism, see Modularity with MEF for WPF QuickStart or Modularity with Unity for WPF QuickStart. To learn more about QuickStarts, see Modularity QuickStarts for WPF .
For information about the Prism library functions used in building modular applications, see " Modules " in " Extending Prism ."