Early 2014, the Patterns & Practices teams at Microsoft released a new version of the Prism library, dedicated to the creation of business apps using C# and XAML on Windows 8.1 (a version for Windows 8 already existed since 2013, the team “just” updated the version to add the support of Windows 8.1).

In this article, we’ll provide the basics to use Prism to create a Windows Store application so let’s start !

Architecture

Here is the diagram of a typical Windows Store application that is developed using the Prism library:

 Logical architecture of a Windows Store business app that uses Prism

As you can see, this is a standard architecture: MVVM to separate the concerns of presentation, presentation logic, and model, a data access layer using repositories and some Services proxies to retrieve the data, etc. The good part is that some elements are directly provided by Prism (ViewBase, ViewModelBase, ViewModelLocator, Event Aggregator, etc.) so, you just have to provide the XAML code (View), the logic code (ViewModel) and the business objects (Model).

Creating the application

Creating the application is the same as creating any Windows Store application (File –> New Project) but, once Visual Studio has finished to create the project, you need to add reference to the following Nuget packages:

  • Prism.StoreApps: this is the class library that provides MVVM support with lifecycle management, and core services to a Windows Store app (see here for the list of all the features provided)
  • Prism.PubSubEvents: the Portable Class Library that contains classes that implement event aggregation (again, check here to see the class contains on the library)

image

Once this is done, derive the App class from the MvvmAppBase class, to allow your application to support the MVVM pattern and the core services (navigation, events aggregation, etc.):

sealed partial class App : MvvmAppBase

Don’t forget to change the base class of the file App.xaml too or you’ll get compile errors:

<storeApps:MvvmAppBase
    x:Class="DemoPrism.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DemoPrism"
    xmlns:storeApps="using:Microsoft.Practices.Prism.StoreApps">

</storeApps:MvvmAppBase>

The MvvmAppBase class is an abstract class that define one method that need to be implemented: OnLaunchApplication. This method is used to perform the initial launch of the application so this is where we’ll define the page that’ll be launched on start:

protected override Task OnLaunchApplication(LaunchActivatedEventArgs args)
{
    NavigationService.Navigate("MainPage", null);

    return Task.FromResult<object>(null);
}

This method need to return a Task object so you can use Task.FromResult to return an empty task. To don’t have conflicts with the initial overridden methods, don’t forget to delete the OnLaunched and OnSuspending methods.

The code use the NavigationService property, which is Navigation Service that we can use to perform navigation within the application. Unfortunately, if you try to run this code, you’ll get the following exception:

SNAGHTML2de0d500

Indeed, by default, Prism use a particular convention for the Views: they need to be placed on the “Views” folder. In my case, I used to have the following structure on my applications:

image

So I have a “Views” folder but this folder contains sub-folders containing the big features of the application. So I need to find a way to tell the Prism library to look for the views on that sub-folders. And this can be done by overriding the GetPageType method of the MvvmAppBase class:

protected override Type GetPageType(string pageToken)
{
    var subPageToken = pageToken.Substring(0, pageToken.Length - pageToken.IndexOf("Page", StringComparison.Ordinal));

    var assemblyQualifiedAppType = this.GetType().GetTypeInfo().AssemblyQualifiedName;
    var pageNameWithParameter = assemblyQualifiedAppType.Replace(this.GetType().FullName, this.GetType().Namespace + ".Views.{0}.{1}");
    var viewFullName = string.Format(CultureInfo.InvariantCulture, pageNameWithParameter, subPageToken, pageToken);
    var viewType = Type.GetType(viewFullName);

    return viewType;
}

Now, if I execute the code, the application loads correctly and the page is displayed:

image

Registering services using Dependency Injection

This is not a mandatory steps but Prism has been a long time co-worker of Dependency Injection tools like Unity. So let’s start by adding the Nuget package of your choice (here, I’ll use Unity):

image

Then, declare a Unity container to register and resolve types and instances:

private readonly IUnityContainer _container = new UnityContainer();

You can then override the OnInitialize method of MvvmAppBase to register types and perform any other initialization:

protected override void OnInitialize(IActivatedEventArgs args)
{
    _container.RegisterInstance<INavigationService>(NavigationService);
    _container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
}

And, to be able to resolve the types, you just need to override the Resolve method:

protected override object Resolve(Type type)
{
    return _container.Resolve(type);
}

Now, let’s see how we can add the Views and the ViewModels to the application.

Creating a View and its ViewModel

The Views has nothing particular related to the Prism library. You just composed it like you used to do it so this is just XAML content that you defined using Visual Studio or Blend.

To define the ViewModel associated to the view, you can use the ViewModelLocator.AutoWireViewModel property:

xmlns:storeApps="using:Microsoft.Practices.Prism.StoreApps"
storeApps:ViewModelLocator.AutoWireViewModel="true"

Once this property is set to True, the ViewModelLocator will try to instanciate the corresponding ViewModel based on a particular convention: the ViewModels need to be located in the same assembly, in a namespace ended by .ViewModels and the ViewModel name must match the View name and ends with ViewModel. But this is always what you have in your application. Indeed, you can have your ViewModels in a separate assembly or you can have them within the folder containing the views, like I use to have:

image

Fortunately, you can once again change the way Prism is loading your ViewModels, by specifying the ViewModelLocator.SetDefaultViewTypetoViewModelTypeResolver method:

protected override void OnInitialize(IActivatedEventArgs args)
{
    _container.RegisterInstance<INavigationService>(NavigationService);
    _container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());

    ViewModelLocator.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
    {
        var subPageToken = viewType.Name.Substring(0, viewType.Name.Length - viewType.Name.IndexOf("Page", StringComparison.Ordinal));

        var assemblyQualifiedAppType = this.GetType().GetTypeInfo().AssemblyQualifiedName;
        var pageNameWithParameter = assemblyQualifiedAppType.Replace(this.GetType().FullName, this.GetType().Namespace + ".Views.{0}.{1}ViewModel");

        var viewFullName = string.Format(CultureInfo.InvariantCulture, pageNameWithParameter, subPageToken, viewType.Name);
        var viewModelType = Type.GetType(viewFullName);

        return viewModelType;
    });
}

Now, if you execute your application and put a breakpoint to the ViewModel’s constructor, you’ll see that the execution is stopped correctly (the ViewModel has been correctly instantiated):

image

Also, thanks to the Dependency Injection we put in place with Unity, we can construct the ViewModel and get, in constructor’s parameters, the type we want to retrieve:

public class MainPageViewModel : ViewModel
{
    private readonly INavigationService _navigationService;
    private readonly IEventAggregator _eventAggregator;

    public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
    {
        this._navigationService = navigationService;
        this._eventAggregator = eventAggregator;
    }
}

image

Handling navigation between pages

To handle navigation between pages, Prism library offers to developer the interface INavigationAware (already implemented by the base ViewModel class). This interface exposes 2 methods:

  • OnNavigatedFrom: which is called before the navigation occurred. You can use this method to preserve the state of your data during the navigation
  • OnNavigatedTo: which is called after the navigation to a page is performed. This method can be used to retrieve parameter sent from a page to another page.

So this interface is really helpful if you want to execute navigation to another page within a component which has nothing to do with navigation (like a ViewModel for example!). And to change to another page, we’ll use the methods provided by the INavigationService (that is injected to the ViewModel’s constructor thanks to the dependency injection):

image

Saving properties’ values between the application’s states

As you know, a Windows Store application have different state: Not Running, Running, Suspended and Terminated. This is your job to preserve the properties’ value between the different states of your application so when users are going back to the application, they don’t feel there was a kind of pause (or termination) during the application’s execution.

So let’s take a look at the following example:

private string _mySuperString;

public string MySuperString
{
    get
    {
        return this._mySuperString;
    }
    set
    {
        this.SetProperty(ref this._mySuperString, value);
    }
}

And the dedicated XAML interface:

<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
            VerticalAlignment="Center">
    <TextBox HorizontalAlignment="Center"
             VerticalAlignment="Center"
             Text="{Binding MySuperString, Mode=TwoWay}" />
    <Button Content="Click Me!"
            HorizontalAlignment="Center"
            VerticalAlignment="Center" />
</StackPanel>

If I enter some text on the Textbox, the property will be updated in memory, thanks to the databinding feature. Each time the application is suspended and  terminated by the OS (which determined when to kill suspended apps to retrieve some resources), the text entered in the Textbox is deleted from the memory. To stop that, I can simply add the RestoreState attribute to the property:

private string _mySuperString;

[RestorableState]
public string MySuperString
{
    get
    {
        return this._mySuperString;
    }
    set
    {
        this.SetProperty(ref this._mySuperString, value);
    }
}

image

Now, I can execute the application and simulate a termination done by the OS, by using the Suspend and shutdown feature from the Debug Location toolbar:

image

If I execute the application again, without having to add other code, the MySuperString property will be automatically populated and the user interface will be modified:

image

Of course, you can also add more complex objects to the State to retrieve them later. For that, you’ll need to use the ISessionStateService (which exposes a Dictionary object) that manages the automatic load and save during application exit and startup!

First, to get the ISessionStateService object from your ViewModel, you need to get it injected to the constructor:

public MainPageViewModel(INavigationService navigationService, ISessionStateService sessionStateService)
{
    this._navigationService = navigationService;
    this._sessionStateService = sessionStateService;
}

Don’t forget to register the instance during the app’s initialization:

protected override void OnInitialize(IActivatedEventArgs args)
{
    _container.RegisterInstance<INavigationService>(NavigationService);
    _container.RegisterInstance<ISessionStateService>(SessionStateService);
}

Now, you just need to add your object to the session’s state:

public void SaveToSessionState()
    {
        var users = new List<User> { new User { Id = Guid.NewGuid(), Name = "Thomas LEBRUN" } };

        this._sessionStateService.SessionState.Add("DemoKey", users);
    }

public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

And, finally, you can check (when the user navigated to the ViewModel) if the key exists in the dictionary:

public override void OnNavigatedTo(object navigationParameter, NavigationMode navigationMode, Dictionary<string, object> viewModelState)
{
    base.OnNavigatedTo(navigationParameter, navigationMode, viewModelState);

    if (_sessionStateService.SessionState.ContainsKey("DemoKey"))
    {
        var users = _sessionStateService.SessionState["DemoKey"] as List<User>;
        if (users != null)
        {
            //
        }
    }
}

Now, if you execute the code and simulate an termination of the app, you got a correct execution… or not :)

image

You’ll see that an exception is raised:

image

If you take a look at the details of the exception, you’ll see something familiar if you use to work with data serialization:

image

“Type ‘System.Collections.Generic.List`1[[DemoPrism.Views.Main.User, DemoPrism, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]’ with data contract name ‘ArrayOfUser:http://schemas.datacontract.org/2004/07/DemoPrism.Views.Main’ is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.”

Indeed, to save the session state, the system will serialize the items and here, we try to serialize a type (User) that is not known by the DataContractSerializer. So we need to teach it all about our custom type(s). This is done by overriding the method OnRegisterKnownTypesForSerialization of the MvvmAppBase class:

protected override void OnRegisterKnownTypesForSerialization()
{
    base.OnRegisterKnownTypesForSerialization();

    SessionStateService.RegisterKnownType(typeof(User));
    SessionStateService.RegisterKnownType(typeof(List<User>));
}

And now, the termination of the app works successfully and when the user navigated to the page, we can check/retrieve the results from the session state:

image

Simple but terribly useful isn’t it ?! Now, let’s see how we can communicate from a component to another one!

Communication between components using the Event Aggregator

Sometimes, you might need to communicate some data from a component to another one. This can be from ViewModelA to ViewModelB, ViewModelA to ViewC, ViewB to ViewA, etc. All this elements have one thing in common: they are not loosely coupled so there are no links between them. So how can we communicate from a component to another one when they are not linked ?

The answer is 2 words: Event Aggregator. This is a design pattern, also known as Publish/Subscribe, which is used to send a message (from the publisher) that is catched by all the subscribers (Martin Fowler talked about the pattern on his blog).

So how does it works ? Well, first, you need to get a reference to the Event Aggregator instance. This can be done through the dependency injection of the ViewModel’s constructor:

public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator, ISessionStateService sessionStateService)
{
    this._navigationService = navigationService;
    this._eventAggregator = eventAggregator;
    this._sessionStateService = sessionStateService;
}

Or thanks to Dependency Injection container you choose to use:

var eventAggregator = UnityServiceHelper.UnityContainer.Resolve<IEventAggregator>();

Then, we need to create the event object that will be sent from the publisher to the subscribers. This object is a simple class that inherits from the PubSubEvent<T> class:

public class SessionStateSavedEvent : PubSubEvent<object>
{
}

Now, we just have to publish an instance of this event. To do so, we’ll use the GetEvent method of the EventAggregator object and we’ll call the Publish method:

this._eventAggregator.GetEvent<SessionStateSavedEvent>().Publish(null);

This method takes in parameter the object you want to send to the subscribers (in our case, we don’t want to use any parameters so we send the null value). Finally, we need to add a handler that will be raised when the event will be catched by the subscriber:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    var eventAggregator = UnityServiceHelper.UnityContainer.Resolve<IEventAggregator>();
    eventAggregator.GetEvent<SessionStateSavedEvent>().Subscribe(OnSessionStateSavedEventSubscribe);
}

private void OnSessionStateSavedEventSubscribe(object obj)
{
    //
}

And voilà! Once the publisher send an instance of our event, all the subscribers will be notified and their handlers will be invoked:

image image

Of course, there are plenty other possibilities when using the Event Aggregator like specifying on which thread the handler will be executed or like filtering which handlers need to be executed:

image

image

 

Prism allows a lot of more (validation of user input, performance optimizations, etc.) but I hope this first overview gave you all you need to take a further look at this very cool library !

 

Happy coding!

 

PS: The source code of the article is available here.

As I’ve explained before, I’ll be presenting 3 sessions during the next TechDays in France.

One of the session is entitled “Améliorer votre productivité XAML en entreprise” and we are currently working on it. If you plan to attend to our session (or to TechDays), we want to learn from you: we give you the opportunity to let us know what you want to see in the session !

If you have any particular needs or if you have some subjects that you want to see covered in the session, let me know (in a comment) and we’ll try do our best !

Thanks and don’t forget: if you plan to come in TechDays 2012, feel free to come to see me.

 

Happy coding!

Prism is a well-know framework to develop applications that support modularity, navigation, communication between loosely coupled components and more !

Each module is loaded automatically (OnDemand property of Module attribute set to false) or on demand (OnDemand set to true) but in all case, you can’t define in which order the modules are loaded. By default, they are loaded by using their name and you don’t have any way to change that.

In my case, I wanted to be able to load my modules in specific order. So, after taking a look on Internet, I’ve found this post on StackOverFlow: http://stackoverflow.com/questions/1296642/how-to-control-the-order-of-module-initialization-in-prism

The code work fine except for modules that are asked to be loaded on demand. So I needed to find a way to be able to specify the loading order of each module, even if they have to be started on demand) and, for that, I’ve been inspired by the previous code.

Indeed, I first create the Priority attribute:

/// <summary>
/// Allows the order of module loading to be controlled.  Where dependencies
/// allow, module loading order will be controlled by relative values of priority
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class PriorityAttribute : Attribute
{
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="priority">the priority to assign</param>
    public PriorityAttribute(int priority)
    {
        this.Priority = priority;
    }

    /// <summary>
    /// Gets or sets the priority of the module.
    /// </summary>
    /// <value>The priority of the module.</value>
    public int Priority { get; private set; }
}

Then, I created a dedicated ModuleCatalog, inspired by the PrioritizedDirectoryCatalog (from the post of StackOverFlow) and the DirectoryModuleCatalog (from the Prism’s sources):

public class PrioritizedDirectoryModuleCatalog : DirectoryModuleCatalog
{
    protected override void InnerLoad()
    {
        if (string.IsNullOrEmpty(this.ModulePath))
            throw new InvalidOperationException("The ModulePath cannot contain a null value or be empty.");

        if (!Directory.Exists(this.ModulePath))
            throw new InvalidOperationException(
                string.Format(CultureInfo.CurrentCulture, "Directory {0} was not found.", this.ModulePath));

        AppDomain childDomain = this.BuildChildDomain(AppDomain.CurrentDomain);

        try
        {
            List<string> loadedAssemblies = new List<string>();

            var assemblies = (
                                 from Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()
                                 where !(assembly is System.Reflection.Emit.AssemblyBuilder)
                                    && assembly.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder"
                                    && !String.IsNullOrEmpty(assembly.Location)
                                 select assembly.Location
                             );

            loadedAssemblies.AddRange(assemblies);

            Type loaderType = typeof(ModulePriorityLoader);

            if (loaderType.Assembly != null)
            {
                var loader =
                    (ModulePriorityLoader)
                    childDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap();
                loader.LoadAssemblies(loadedAssemblies);
                this.Items.AddRange(this.Sort(loader.GetModuleInfos(this.ModulePath)));
            }
        }
        finally
        {
            AppDomain.Unload(childDomain);
        }
    }

    /// <summary>
    /// Sort modules according to dependencies and Priority
    /// </summary>
    /// <param name="modules">modules to sort</param>
    /// <returns>sorted modules</returns>
    protected override IEnumerable<ModuleInfo> Sort(IEnumerable<ModuleInfo> modules)
    {
        Dictionary<string, int> priorities = GetPriorities(modules);

        //call the base sort since it resolves dependencies, then re-sort 
        var result = new List<ModuleInfo>(base.Sort(modules));
        result.Sort((x, y) =>
        {
            string xModuleName = x.ModuleName;
            string yModuleName = y.ModuleName;

            //if one depends on other then non-dependent must come first
            //otherwise base on priority
            if (x.DependsOn.Contains(yModuleName))
                return 1; //x after y
            else if (y.DependsOn.Contains(xModuleName))
                return -1; //y after x
            else
                return priorities[xModuleName].CompareTo(priorities[yModuleName]);
        });

        return result;
    }

    /// <summary>
    /// Get the priorities
    /// </summary>
    /// <param name="modules"></param>
    /// <returns></returns>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
    public Dictionary<string, int> GetPriorities(IEnumerable<ModuleInfo> modules)
    {
        //retrieve the priorities of each module, so that we can use them to override the 
        //sorting - but only so far as we don't mess up the dependencies
        var priorities = new Dictionary<string, int>();
        var assemblies = new Dictionary<string, Assembly>();

        foreach (ModuleInfo module in modules)
        {
            if (!assemblies.ContainsKey(module.Ref))
            {
                //LoadFrom should generally be avoided appently due to unexpected side effects,
                //but since we are doing all this in a separate AppDomain which is discarded
                //this needn't worry us
                assemblies.Add(module.Ref, Assembly.LoadFrom(module.Ref));
            }

            Type type = assemblies[module.Ref].GetExportedTypes()
                .Where(t => t.AssemblyQualifiedName.Equals(module.ModuleType, StringComparison.Ordinal))
                .First();

            var priorityAttribute =
                CustomAttributeData.GetCustomAttributes(type).FirstOrDefault(
                    cad => cad.Constructor.DeclaringType.FullName == typeof(PriorityAttribute).FullName);

            int priority;
            if (priorityAttribute != null)
            {
                priority = (int)priorityAttribute.ConstructorArguments[0].Value;
            }
            else
            {
                priority = 0;
            }

            priorities.Add(module.ModuleName, priority);
        }

        return priorities;
    }

    /// <summary>
    /// Local class to load assemblies into different appdomain which is then discarded
    /// </summary>
    private class ModulePriorityLoader : MarshalByRefObject
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        internal void LoadAssemblies(IEnumerable<string> assemblies)
        {
            foreach (string assemblyPath in assemblies)
            {
                try
                {
                    Assembly.ReflectionOnlyLoadFrom(assemblyPath);
                }
                catch (FileNotFoundException)
                {
                    // Continue loading assemblies even if an assembly can not be loaded in the new AppDomain
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        internal ModuleInfo[] GetModuleInfos(string path)
        {
            DirectoryInfo directory = new DirectoryInfo(path);

            ResolveEventHandler resolveEventHandler =
                delegate(object sender, ResolveEventArgs args) { return OnReflectionOnlyResolve(args, directory); };

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;

            Assembly moduleReflectionOnlyAssembly =
                AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().First(
                    asm => asm.FullName == typeof(IModule).Assembly.FullName);

            Type IModuleType = moduleReflectionOnlyAssembly.GetType(typeof(IModule).FullName);

            IEnumerable<ModuleInfo> modules = GetNotAllreadyLoadedModuleInfos(directory, IModuleType);

            var array = modules.ToArray();
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;

            return array;
        }

        private static IEnumerable<ModuleInfo> GetNotAllreadyLoadedModuleInfos(DirectoryInfo directory, Type IModuleType)
        {
            List<FileInfo> validAssemblies = new List<FileInfo>();
            Assembly[] alreadyLoadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();

            var fileInfos = directory.GetFiles("*.dll")
                .Where(file => alreadyLoadedAssemblies
                                   .FirstOrDefault(
                                   assembly =>
                                   String.Compare(Path.GetFileName(assembly.Location), file.Name,
                                                  StringComparison.OrdinalIgnoreCase) == 0) == null);

            foreach (FileInfo fileInfo in fileInfos)
            {
                Assembly assembly = null;
                try
                {
                    assembly = Assembly.ReflectionOnlyLoadFrom(fileInfo.FullName);
                    validAssemblies.Add(fileInfo);
                }
                catch (BadImageFormatException)
                {
                    // skip non-.NET Dlls
                }
            }

            return validAssemblies.SelectMany(file => Assembly.ReflectionOnlyLoadFrom(file.FullName)
                                        .GetExportedTypes()
                                        .Where(IModuleType.IsAssignableFrom)
                                        .Where(t => t != IModuleType)
                                        .Where(t => !t.IsAbstract)
                                        .Select(type => CreateModuleInfo(type)));
        }

        private static Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
        {
            Assembly loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(
                asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
            if (loadedAssembly != null)
            {
                return loadedAssembly;
            }

            AssemblyName assemblyName = new AssemblyName(args.Name);
            string dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
            if (File.Exists(dependentAssemblyFilename))
            {
                return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
            }
            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        private static ModuleInfo CreateModuleInfo(Type type)
        {
            string moduleName = type.Name;
            List<string> dependsOn = new List<string>();
            bool onDemand = false;
            var moduleAttribute =
                CustomAttributeData.GetCustomAttributes(type).FirstOrDefault(
                    cad => cad.Constructor.DeclaringType.FullName == typeof(ModuleAttribute).FullName);

            if (moduleAttribute != null)
            {
                foreach (CustomAttributeNamedArgument argument in moduleAttribute.NamedArguments)
                {
                    string argumentName = argument.MemberInfo.Name;
                    switch (argumentName)
                    {
                        case "ModuleName":
                            moduleName = (string)argument.TypedValue.Value;
                            break;

                        case "OnDemand":
                            onDemand = (bool)argument.TypedValue.Value;
                            break;

                        case "StartupLoaded":
                            onDemand = !((bool)argument.TypedValue.Value);
                            break;
                    }
                }
            }

            var moduleDependencyAttributes =
                CustomAttributeData.GetCustomAttributes(type).Where(
                    cad => cad.Constructor.DeclaringType.FullName == typeof(ModuleDependencyAttribute).FullName);

            foreach (CustomAttributeData cad in moduleDependencyAttributes)
            {
                dependsOn.Add((string)cad.ConstructorArguments[0].Value);
            }

            ModuleInfo moduleInfo = new ModuleInfo(moduleName, type.AssemblyQualifiedName)
            {
                InitializationMode =
                    onDemand
                        ? InitializationMode.OnDemand
                        : InitializationMode.WhenAvailable,
                Ref = type.Assembly.CodeBase,
            };
            moduleInfo.DependsOn.AddRange(dependsOn);

            return moduleInfo;
        }
    }
}

Now, in your Bootstrapper, you simply have to use your new ModuleCatalog:

protected override IModuleCatalog CreateModuleCatalog()
{
    return new PrioritizedDirectoryModuleCatalog { ModulePath = ".\Modules" };
}

Then, decorate each of your module with this new attribute:

[Module(ModuleName = "RootModule", OnDemand = false)]
[Priority(-1)]
public class RootModule : IModule

[Module(ModuleName = "ModuleA", OnDemand = true)]
[Priority(1)]
public class ModuleAModule : IModule

All that’s all ! If you enumerate all the modules, you will be able to see that they are ordered as you define it: RootModule will be the first, then, ModuleA (even if the sort on the name will produce a different result):

[Dependency]
public IModuleCatalog ModuleCatalog { get; set; }

[Dependency]
public IModuleManager ModuleManager { get; set; }

foreach (var module in this.ModuleCatalog.Modules.Where(m => m.InitializationMode != InitializationMode.WhenAvailable))
{
    this.ModuleManager.LoadModule(module.ModuleName);
}

 

Happy coding!