Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prism 2.1 Injecting Modules into ViewModel

Tags:

c#

mvvm

prism

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

like image 983
Robert Reid Avatar asked Oct 26 '22 21:10

Robert Reid


2 Answers

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.

like image 87
Anderson Imes Avatar answered Nov 15 '22 05:11

Anderson Imes


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.

like image 39
scim Avatar answered Nov 15 '22 07:11

scim