prism/modules with catel

Topics: Questions
Jun 27, 2012 at 2:51 PM

Very simple question, but I am absolutely new to prism/modules: Do I need Prism with MEF to use the module loading from a directory/folder? Or can this done without an external container?

For RegionManager I need to use Prism, or build sth. like this, I think.

 

Jun 27, 2012 at 3:29 PM

You can do it without any external container. Actually, in RC 2 of Catel 3.2 we will include a custom bootstrapper for Catel. Then you only have to use this in the bootstrapper and you are done:

 

        /// <summary>
        /// Creates the <see cref="T:Microsoft.Practices.Prism.Modularity.IModuleCatalog"/> used by Prism.
        /// </summary>
        /// <returns></returns>
        protected override IModuleCatalog CreateModuleCatalog()
        {
            var moduleCatalog = new DirectoryModuleCatalog { ModulePath = @".\" + ModuleBase.ModulesDirectory};

            moduleCatalog.Initialize();

            return moduleCatalog;
        }

Jun 27, 2012 at 4:16 PM

Very nice! Do you have an aspired date for releasing RC2? Or can I already get it via nuget?

Jun 27, 2012 at 4:51 PM

Hopefully tonight, but can't promise anything. Follow @CatelMvvm on twitter to make sure to get the latest info.

Jun 27, 2012 at 5:33 PM

Just pushed a version to nuget with the first implementation of the bootstrapper. Docs and a generic bootstrapper (which can be used to simplify shell instantiation) will follow soon.

Jun 28, 2012 at 11:41 AM
Edited Jun 28, 2012 at 11:42 AM

Ok I now have the 3.2 version. With custom bootstrapper you mean the bootstrapperbase/<>? Or is there sth. else? Are you going to offer an example how to use it, or do we have to read the prism manual?

Jun 28, 2012 at 11:49 AM

3.2 RC 2 has BootstrapperBase<TShell>, you can use it. 

We have the docs written, just not yet published. Just use it as my earlier example, it is enough:

public class MyBootstrapper : BootstrapperBase<MainWindow>
{
        protected override IModuleCatalog CreateModuleCatalog()
        {
            var moduleCatalog = new DirectoryModuleCatalog { ModulePath = @".\" + ModuleBase.ModulesDirectory};

            moduleCatalog.Initialize();

            return moduleCatalog;
        }
}

Jun 28, 2012 at 12:01 PM

Yes thanks, that way I did it. By the way, where does "ModuleBase.ModulesDirectory" came from? Can't find any reference for it. ModelBase is in Catel.Modules, but where comes the extension from?

"

Jun 28, 2012 at 12:08 PM

Just a constant I defined, stands for ".\Modules". 

Jun 28, 2012 at 1:12 PM
Edited Jun 28, 2012 at 1:29 PM

You have a IModuleTracker in the dll, but why are there the two methods "void RecordModuleLoaded(string moduleName);" and "void RecordModuleDownloading(string moduleName, long bytesReceived, long totalBytesToReceive);" missing?

 

Second, a sw architecture question. My application is basically 3-layer with a dal. Now i want to make modules with views/vm which only some customers get, so should I add all new seperated in the module like dal, or can I refer from module to my app dal?

Same with services. Should a service, which lies in a seperate assembly have it's own db access? Or should he reference the app BAL/DAL?

Jun 28, 2012 at 1:31 PM
Edited Jun 28, 2012 at 1:50 PM

Indeed, we are aware about that and we will add them asap.

 

The second, for me the best way is to have one DAL assembly that you can refer in all the modules you want to have. Commonly, we make an infrastucture project (a class library) which shelter in your case a reference of DAL and perhaps the proxy class of your service. This way, you can be sure to have the same definition of the DAL in all your modules.

Jun 28, 2012 at 2:48 PM
Edited Jun 28, 2012 at 2:54 PM

Thanks.

Next question: I am now trying to use the moduleBase for my modules. what is the meaning of the IContainer property there? For what to use it?

Jun 28, 2012 at 4:00 PM
Edited Jun 28, 2012 at 4:53 PM

This property is there to contain the IoC Container you have choice to use.

In the case of Unity, we will be able to do this:

 ModuleBase<IUnityContainer> and in your derived class, you will have a Unity Container in the property.

you will do something like this after for example:

 

protected override void OnRegisterViewsAndTypes()
{
     // Register other view objects
     Container.RegisterType<Object, SettingsRibbonTab>("SettingsRibbonTab");
}

Jun 28, 2012 at 4:28 PM

Ah allright.

Ok, I have created my module: ModuleBase. In my bootstrapper: BoostrapperBase I have the overridden CreatemoduleCatalog for search in adirectory. In my directory i have the assembly with my module.

But now, how can I make the module visible for the DirectoryModuleCatalog. With MEF I would use Attribut [ModuleExport(typeof(ModuleB), InitializationMode = InitializationMode.OnDemand)], with catel servicelocator?

Jun 28, 2012 at 4:29 PM

It is automatically visible. The ModuleBase is decorated with the Module attribute, so all modules are located in the ModuleCatalog.

Jun 28, 2012 at 4:33 PM

I thougt this too, but my catalog is empty, Ok have to check it.

Jun 28, 2012 at 4:37 PM

You did initialize the catalog, right?

moduleCatalog.Initialize();
Jun 28, 2012 at 9:02 PM

See this for docs:

http://catel.catenalogic.com/index.htm?catel_extensions_prism.htm

Jun 29, 2012 at 8:03 AM
Edited Jun 29, 2012 at 9:49 AM

 

[Module(OnDemand = true, ModuleName = "FreightBuildingModule")] 
public class FreightBuildingModule: ModuleBase { public FreightBuildingModule() : base("FreightBuildingModule", ServiceLocator.Instance.ResolveType<IModuleTrackerService>(), ServiceLocator.Instance) {

 

ServiceLocator.Intance implements IServiceLocator, but for the constructor I need IContainer? That's the same I mentioned before. I think I am stupid at this point.

Next, I got newest packages 10min ago, but there is no generic ModuleBase?

Jun 29, 2012 at 8:15 AM
Edited Jun 29, 2012 at 8:22 AM

Ah, ok. just downloaded the sourcecode, and there is everything ok, IContainer is now IServiceLocator and the generic moduleBase is also there. Do you put it on nuget, or will it take some time, because I get compile errors with your sourcecode..

Jun 29, 2012 at 8:50 AM
Edited Jun 29, 2012 at 10:00 AM

And, how can I make my module loaded on demand, and not immediately when available?

Because I get an error while loading "{"Activation error occured while trying to get instance of type FreightBuildingModule, key \"\""}"

with this code:

var moduleManger = ServiceLocator.Instance.ResolveType<IModuleManager>();
moduleManger.LoadModule("FreightBuildingModule");

In the ModelCataloge the module is present.

Edit: Can't load the module immediately, too.

Jun 29, 2012 at 3:56 PM

The feature of load modules on demand is part of PRISM. If you use ConfigurationModuleCatalog instead DirectoryModuleCatalog you should got this goal with ease.


ConfigurationModuleCatalog configurationCatalog = new ConfigurationModuleCatalog();


Next snippet code snippet is part of the sample applications that comes with PRISM setup. Notice the startupLoaded attribute set to "false".

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="modules" type="Microsoft.Practices.Prism.Modularity.ModulesConfigurationSection, Microsoft.Practices.Prism"/>
  </configSections>
  <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>
</configuration>


You can reach this goal also in SL application  using


Modularity.ModuleCatalog.CreateFromXaml(new Uri("/ModularityWithUnity.Silverlight;component/ModulesCatalog.xaml", UriKind.Relative));

with this file as resource


 <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>

    <!-- Module info without a group -->
    <Modularity:ModuleInfo Ref="ModuleD.xap" ModuleName="ModuleD" ModuleType="ModuleD.ModuleD, ModuleD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</Modularity:ModuleCatalog>

We are working on a CompositeModuleCatalog  that allow us aggregate module catalogs.

CompositeModuleCatalog moduleCatalog =new CompositeModuleCatalog();

moduleCatalog.Add(new DirectoryModuleCatalog(...));

moduleCatalog.Add(new ConfigurationModuleCatalog(...))

moduleCatalog.Add(new ....)

moduleCatalog.Initialize();

Jun 29, 2012 at 4:44 PM
Edited Jun 29, 2012 at 4:47 PM

Thanks for your reply. What do you mean with "is part of prism"? ModuleManager.LoadModule is also directly from Prism assembly.

With your solution, I have to write extra lines in config. With directoryCatalog, I don't need to do so. And I works like a charm.

But I will try it. How do you load the module then? For me in your post there is no loading, or am I wrong?

Jun 29, 2012 at 6:12 PM

Catel just provides an extension allowing Catel and PRISM go hand in hand. So, load module on demands is part of PRISM (http://compositewpf.codeplex.com/) is not actually a Catel feature.

YES, there are not loading code on my post. I just commet you about how delay the load of a module. To load a module you must call ModuleManager.LoadModule as you know.

 

Jul 3, 2012 at 7:12 AM

I think my question get lost. When will the new stuff like the generic moduleBase be available with nuget? Need it.

Jul 3, 2012 at 9:14 AM

We plan to deploy a new version via nuget on wednesday. For now, just copy the source code if you really need it. You can remove it again when you link to the new nuget package.

Jul 23, 2013 at 9:47 AM
Hello,

i miss the constant "ModuleBase.ModulesDirectory" in Catel 3.6? Is it removed from ModuleBase?

Kind regards
Siddy
Jul 24, 2013 at 11:38 AM
Good question, I think it was never there. I checked the log in source control, couldn't find it. Are you sure it was there? If so, in which version? Otherwise just add it yourself, the value would have been "Modules"
Jul 24, 2013 at 10:19 PM
I don't remember such constant.
Jul 25, 2013 at 8:59 AM
Hi GeertvanHorrik,

in your example: https://catelproject.atlassian.net/wiki/display/CTL/Using+the+bootstrapper you have defined this constant in this methode:

protected override IModuleCatalog CreateModuleCatalog()
{
    var moduleCatalog = new DirectoryModuleCatalog { ModulePath = @".\" + ModuleBase.ModulesDirectory};
    moduleCatalog.Initialize();
    return moduleCatalog;
}
this example inheritance from BootstrapperBase and this Base class is in your Catel Framework?

OK, i defin this constant in my own base class ;-).

With kind regards,
Siddy
Jul 25, 2013 at 9:02 AM
Hmmm, good notice. This is because for the example I used a custom module which I put in my own constant. I have updated the docs.
Jul 26, 2013 at 12:31 PM
Hello again,

i have a another question. In the Methode

protected override void ConfigureModuleCatalog()
   {
       base.ConfigureContainer();
   }
i get a serviceLocator null pointer Exception when i call base.ConfigureContainer()

You have any idea?


I my Bootstrapper constructor i try:

CreatedShell += (sender, e) =>
        {
            var statusBarService = ServiceLocator.Default.ResolveType<IStatusBarService>();
            statusBarService.UpdateStatus("Ready");
        };
but CreatedShell does not exsist in the current context ... is it not in the base class included?


Thanks 4 help and greetings
Siddy
Jul 26, 2013 at 12:42 PM
the StackTrace of NullPointer Exception:


at Catel.Argument.IsNotNull(String paramName, Object paramValue)
at Catel.IoC.ServiceLocatorExtensions.RegisterInstance[TService](IServiceLocator serviceLocator, TService instance)
at Catel.BootstrapperBase.ConfigureContainer()
at Catel.BootstrapperBase`1.ConfigureContainer()
at VAPImporter.VAPImporterBootstrapper.ConfigureModuleCatalog() in D:\Projects\VAPImporter\VAPImporter.Shell\VAPImporterBootstrapper.cs:line 91
at Catel.BootstrapperBase.<CreateInitializationTasks>b__2(ITaskProgressTracker x)
at Catel.MVVM.Tasks.ActionTask.Execute()
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
Jul 26, 2013 at 1:00 PM
and this i found in my Windows EventLog

Exception Info: System.ArgumentNullException
Stack:
at Catel.Argument.IsNotNull(System.String, System.Object)
at Catel.IoC.ServiceLocatorExtensions.RegisterInstance[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](Catel.IoC.IServiceLocator, System.__Canon)
at Catel.BootstrapperBase.ConfigureContainer()
at Catel.BootstrapperBase`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].ConfigureContainer()
at VAPImporter.VAPImporterBootstrapper.ConfigureModuleCatalog()
at Catel.BootstrapperBase.<CreateInitializationTasks>b__2(Catel.MVVM.Tasks.ITaskProgressTracker)
at Catel.MVVM.Tasks.ActionTask.Execute()
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(System.Object)
at System.Threading.ExecutionContext.runTryCode(System.Object)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
at MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)
at System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(System.Windows.Interop.MSG ByRef)
at System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
at System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
at System.Windows.Application.RunDispatcher(System.Object)
at System.Windows.Application.RunInternal(System.Windows.Window)
at System.Windows.Application.Run(System.Windows.Window)
at System.Windows.Application.Run()
at MyApp.App.Main()
at System.AppDomain._nExecuteAssembly(System.Reflection.RuntimeAssembly, System.String[])
at System.AppDomain.ExecuteAssembly(System.String, System.Security.Policy.Evidence, System.String[])
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()
Jul 26, 2013 at 1:09 PM
Edited Jul 26, 2013 at 1:09 PM
Please create an issue at https://catelproject.atlassian.net/secure/Dashboard.jspa and we will take a look at it.
Jul 27, 2013 at 6:08 AM
Hi, I think you are doing something wrong. 1) You are doing a cross method call when you call the base one. Notice your are calling to the base.ConfigureContainer() on ConfigureModuleCatalog. 2) Your goal can be done using in different approach. 2.0) Override
the ConfigureContainer method on the bootstrapper class and register your custom service. 2.1) Ensure your Shell inherits from DataWindow (or UserControl in SL) from Catel 2.2) Create a viewmodel for the Shell following the naming conventions. 2.3) Add a constructor
with one IServiceLocator argument (and call the base constructor) to ensure that the same service locator in bootstrapper be injected in the viewmodel. 2.4) Override the Initialize method of the viewmodel and write your ready code using your service. It should
works, but if doesn't just let us know.
Jul 27, 2013 at 2:21 PM