tags:

views:

737

answers:

4

Hi all,

I've been trying to inject the modules from my ModuleCatalog into my Shell's ViewModel but I'm not having much luck...

I'm creating the ModuleCatalog in my Bootstrapper and my module is getting onto the screen from its Initializer without problem. However, I'd love to be able to bind my list of modules to a container with a DataTemplate which allowed them to be launched from a menu!

Here's my Boostrapper file, I'll be adding more modules as times goes on, but for now, it just contains my rather contrived "ProductAModule":

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        Container.RegisterType<IProductModule>();

        base.ConfigureContainer();
    }

    protected override IModuleCatalog GetModuleCatalog()
    {
        return new ModuleCatalog()
            .AddModule(typeof(ProductAModule));
    }

    protected override DependencyObject CreateShell()
    {
        var view = Container.Resolve<ShellView>();
        var viewModel = Container.Resolve<ShellViewModel>();
        view.DataContext = viewModel;
        view.Show();

        return view;
    }
}

Following on from that, here's my Shell's ViewModel:

public class ShellViewModel : ViewModelBase
{
    public List<IProductModule> Modules { get; set; }

    public ShellViewModel(List<IProductModule> modules)
    {
        modules.Sort((a, b) => a.Name.CompareTo(b));
        Modules = modules;
    }
}

As you can see, I'm attempting to inject a List of IProductModule (to which ProductAModule inherits some of its properties and methods) so that it can then be bound to my Shell's View. Is there something REALLY simple I'm missing or can it not be done using the Unity IoC? (I've seen it done with StructureMap's extension for Prism)

One more thing... When running the application, at the point the ShellViewModel is being resolved by the Container in the Bootstrapper, I receive the following exception:

Resolution of the dependency failed, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Exception message is: The current build operation (build key Build Key[PrismBasic.Shell.ViewModels.ShellViewModel, null]) failed: The parameter modules could not be resolved when attempting to call constructor PrismBasic.Shell.ViewModels.ShellViewModel(System.Collections.Generic.List`1[[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] modules). (Strategy type BuildPlanStrategy, index 3)

Anyway, simple huh... Looks bemused...

Any help would be greatly appreciated!

Rob

A: 

I think I encountered a similar problem today, it turns out that PRISM creates the shell before initializing the modules, so you can't inject any services from the modules into the shell itself.

Try creating another module that depends on all of the others and implements the functionality you want, then you can add it to a region in the shell to display your list of services. Unfortunately I haven't had a chance to try it yet, but this is the solution I plan on implementing.

As a side note, I think you need to mark the property with an attribute to use property injection, but I could be mistake (it's been a while since I played with Unity directly).


Edit: You need to apply the DependencyAttribute to properties to use setter injection in Unity; you can read about it here.

Rory
Hi Rory, thanks for your answer. Sorry, I may have been a little too ambiguous with my question... I'd like that actual modules themselves into the ShellViewModule, I'll tackle services somewhere down the line!The running order of initialisation in the Bootstrapper is as follows:- ConfigureContainer- GetModuleCatalog- CreateShellSo I've definately got my ModuleInfo array to hand, they're just not useful enough for binding against without the additional properties my IProductModule contains.Thanks Rory!
Robert Reid
Hi again, I've used the [Dependency] attribute previously (for Property Injection). I'm trying to inject into the constructor here, I'm guessing that even if I was using Property Injection, it wouldn't be a silver bullet!
Robert Reid
Hey Robert, sorry about the misunderstanding. I just noticed that the docs for that attribute are out of date anyway, so I'll remove the link in a moment. If you take a look at the UnityBootloader's source, you'll notice that the container is configured with the ModuleCatalog, but not the individual modules. Your best bet is probably to override ConfigureContainer and register the modules with the container yourself, just don't forget to call the base implementation.
Rory
Thanks for your advice Rory, I'm a little hesitant to manually add the modules myself, because then I'm responsible for registering and creating the LoggerFacade and adding all of the RegistersTypeIfMissings etc (because calling the base.ConfigureContainer() would re-load all of my modules, causing them to initialize (and more importantly) add their Views to the ShellView regions twice). I really appreciate your help though.
Robert Reid
Not sure if it'll help, but the IModuleCatalog interface has an enumerable Modules property. If you only want the info for the modules, you can have the container inject IModuleCatalog and just use the ModuleInfo items it contains to list the modules.
Rory
I thought that initially, but my IProductModule (the interface from which all of my modules will eventually derive) contains some useful methods for starting the module's controller process flow etc... Oh what a pickle!
Robert Reid
Oh dear, that is a problem. Sorry to say that I'm out of ideas for the night (other than reimplement most of the functionality in the ModuleManager to load the modules yourself, but thats overkill). If you manage to find a solution, please let me know. Out of curiosity, is there any reason you can't move the functionality you mentioned to services inside the various modules? Anyway, its already tomorrow where I am and I need to get to sleep, it's good night and good luck from me.
Rory
Thank you so much for your help Rory, I really do appreciate your persistence! I'll keep you updated!
Robert Reid
Hey Robert, I was hoping to get an answer to my question re moving the "useful methods" out of `IProductModule` and into one of the services each module registers. The idea is that each module could register a service of type `IProductModuleInitializer` using it's module name. You could then get all the instances using Unity's `ResolveAll<T>` method. You could also get a specific instance using the module name (accessible via `IModuleCatalog.Modules`). Obviously only modules that have been loaded will have their services available, but the same would be true for the actual module instance.
Rory
+1  A: 

I think you could probably just do this:

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        Container.RegisterType<IProductModule>();

        base.ConfigureContainer();
    }

    private static ObservableCollection<IProductModule> _productModules = new Obser...();
    public static ObservableCollection<IProductModule> ProductModules
    { 
         get { return _productModules; } 
    }
    protected override IModuleCatalog GetModuleCatalog()
    {
        var modCatalog = new ModuleCatalog()
            .AddModule(typeof(ProductAModule));
        //TODO: add all modules to ProductModules collection

        return modCatalog;
    }

   ...
}

Then you would have a static property that anything could bind to directly, or could be used from your ViewModel.


Here is how to get a list of module names that have been registered with the module catalog.

public class MyViewModel : ViewModel
{

      public ObservableCollection<string> ModuleNames { ... }
      public MyViewModel(IModuleCatalog catalog)
      {
           ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
      }
}

That's pretty much it. IModuleCatalog and IModuleManager are the only things that are setup in the container for you to access in terms of the modules. As I said, though, you won't get any instance data because these modules (hopefully) are yet to be created. You can only access Type data.

Hope this helps.

Anderson Imes
Hi Anderson, there are two reasons why I'm not doing that: 1) I'm writing this in the hope of demystifying Prism with MVVM, so I'm hoping to keep all of my bindings in the View Model (namely the ShellViewModel). 2) If I were to undertake //TODO: add... I wouldn't be using the ModuleCatalog that's registered with the UnityContainer... I do see where you're coming from though and thank you for your suggestion!
Robert Reid
The problem I think I'm facing is the translation of ModuleInfo to IProductModule. They inherit from IProductModule which inherits from IModule. Which just makes the whole situation that bit more irritating!
Robert Reid
@Robert Reid: There's nothing that isn't MVVM about the solution I proposed. This static is just made available for a ViewModel to use and expose to its view OR for the View to use directly using {x:Static...}. As for the modules, the API doesn't store an instance of the IModule (or IProductModule, in your case) only the Type (ModuleInfo.ModuleType) as a string. By this time it has already gotten out the ModuleName (either the type name with no namespaces or the name you have specified in attribute). You can request as a dependency an IModuleCatalog or IModuleManager, but that is all.
Anderson Imes
@Robert Reid: I will post a sample of using the IModuleCatalog to get a list of modules that are available to load, but this is pretty much going to be your limitation... until you actually initialize the modules, they do not exist yet.
Anderson Imes
Hi again Anderson, that would be greatly appreciated. Sure, I know it won't break MVVM best practice (if there were a strict one), I was just thinking of keeping my ViewModel(s) free from relying on classes other than their Models for information. Although there's nothing that says a Bootstrapper can't be I suppose! Thank you very much for your contribution.
Robert Reid
@Robert Reid: I've appended the sample. I hope this sort of clears up what you have access to and what your limitations are.
Anderson Imes
@Robert Reid: Also as to the Model question, I wouldn't normally create a static right in the bootstrapper class... I did this for simplicity. What I normally do is create a model "store" for this info, like Model.ModuleStore or something like that for things to pull from and contribute to. I can see being queazy about pulling directly from Bootstrapper.Modules, for sure.
Anderson Imes
Oh phew! I was starting to question my sanity when you mentioned that! Keeping up with this bleeding edge is hard work! Thanks for appending the sample and thanks for keeping in touch!
Robert Reid
@Robert Reid: It seems like you want a little more information about the modules than is available before they are instantiated. If it's static information, you might consider using Attributes on your type to provide this information. You can do TypeDescriptor.GetAttributes(Type.GetType(moduleInfo.ModuleType)) to get a collection of the attributes decorated on a module type.
Anderson Imes
A: 
var modules = new IProductModule[]
{
    Container.Resolve<ProductAModule>()
    //Add more modules here...
};
Container.RegisterInstance<IProductModule[]>(modules);

That's it! Using this code, I can inject my modules into the ShellViewModel and display each module as a button in my application!

SUCH a simple resolution! From a great guy on the CompositeWPF Discussion group. I recommend them without reserve ^_^

Robert Reid
Except you are creating multiple instances of each module, if you are intending to activate them based on the info displayed. This will likely work, but anything you do in your constrictor for each module will be done twice AND you will permanently have 2 instances of each module type hanging around in memory. This is why I did not suggest this approach. It shows a lack of understanding of how Prism really works.
Anderson Imes
Hi Anderson, I DO have a lack of understanding with Prism! I've tested your theory and there are indeed two instances of my Modules being created. But this is the best solution I've been able to come up with so far (which allows me to tie my modules to their own buttons, each with a command).
Robert Reid
+1  A: 

I think you misunderstood the purpose of the modules. The modules are just containers for the views and services that you wish too use. The shell on the other hand should just contain the main layout of your application.

What I think you should do is to define a region in your shell, and then register the views (which in your case are buttons) with that region.

How you wish do deploy your views and services in terms of modules is more related to what level of modularity you're looking for, i.e. if you want to be able to deploy the views and services of ModuleA independently of the views and services of ModuleB and so on. In your case it might be enough to register everything in one single module.

Take some time to play around with the examples provided with the documentation, they are quite good.

The reason why your examples throws an example is because your ShellViewModel is depending on List and that type is not registered in Unity. Furthermore you're registering IProductModule with Unity, which makes no sense because an Interface cannot be constructed.

scim