I'm trying to set up an application which allows to open multiple instances of the same window (shell). I have a main shell (which is set up by the Bootstrapper) from which I open new instances of my second shell. This second shell contains several regions.
Now there's a problem because a given region can only appear once per application (or RegionManager), so I tried to give each shell its own RegionManager. This seems to work fine, however I'm also using Unity to inject the RegionManager into my ViewModels/Controllers, which means I always get the main shell's instance instead of the one tied to the shell the ViewModel belongs to.
Is it somehow possible make this work? Is this even the right approach for my use case?
There are a couple ways you can handle this. Probably the easiest is to use a Scoped RegionManager:
https://msdn.microsoft.com/en-us/library/ff921162.aspx
http://southworks.com/blog/2011/11/30/prism-region-navigation-and-scoped-regions/
Another is that you can register named instances of IRegionManager, one per shell window, into the container, and resolve those by name. That only really works though if the ViewModels/Services that are dependent on those named instances can only be created within one Window or another.
Another way is to create a child container per window and register a separate RegionManager instance into each child container so that an attempts to resolve instances within a window goes against the region manager for that window.
You can solve this with scoped regions and a custom dialog service.
I really recommend looking into Brian Lagunas' PluralSight course on Prism Problems & Solutions: Showing Multiple Shells on the topic for some nice in-depth details, which is also where this solution was derived from.
To get you started consider the following service:
public interface IShellService
{
void ShowShell();
}
public class ShellService : IShellService
{
private IUnityContainer _unityContainer;
private IRegionManager _regionManager;
public ShellService(IUnityContainer unityContainer, IRegionManager regionManager)
{
_unityContainer = unityContainer;
_regionManager = _regionManager;
}
public void ShowShell()
{
shell = _container.Resolve<Shell>();
var scopedRegion = _regionManager.CreateRegionManager();
RegionManager.SetRegionManager(shell, scopedRegion);
shell.Show();
}
}
Assuming you're using Unity, you will then need to register IShellService with the ContainerControlledLifeTimeManager, which will tell your container to hold on to the instance sent to it (essentially making it a singleton).
Container.RegisterType<IShellService, ShellService>(new ContainerControlledLifeTimeManager());
You'll then simply inject IShellService wherever you need to open a new shell, and call ShowShell() on it.
private void ExecuteShowShellCommand()
{
_shellService.ShowShell();
}
Related
I'm working on a modular GUI application with caliburn.micro and I would like to do the bootstraping process separatly for each module.
The reason for me to do that is that each module should be able to define its own dependencies and it makes little sense for me that the application using it should do i ( except in specific cases)
I can create a child container in the main boostraper by doing
container.CreateChildContainer();
But then I don't see how to register this container for my module to use. Any pointer to how it can be done ?
I can pass the child module to a boostraping method for my module, register what I need but don't know what to do with this container afterward.
Thank you and best regards,
I've found a partially working solution. However, I have some doubt about it beeing the right one :
My modules have a base class, which is registered inside de container of the bootstrapper class. the container register itself so it can be injected in the modules constructor :
private SimpleContainer container = new SimpleContainer();
protected override void Configure()
{
container.Instance<container>();
container.Instance<ILoggerFactory>(loggerFactory);
container.Instance<IConfiguration>(config);
container.Singleton<TestModule>();
}
In my module class , an initialization method is called from the constructor which creates a childContainer and register the required class in it.
public TestModule(IConfiguration config, ILoggerFactory loggerFactory, SimpleContainer mainContainer)
{
_modulecontainer = container.CreateChildContainer();
}
protected override void Initialize()
{
ModuleContainer.Singleton<TesterClass>();
ModuleContainer.Singleton<ServiceTesterViewModel>();
}
This enables me to use constructor injection in my module without adding every details of the module in the application's bootstraper. Also I don't have to declare every viewModel in my module public.
However I'm not sure it's a proper solution as I can't make it work as described in the documentation. According to it I should be able to override a previous declaration however it doesn't work. If I try to get an instance directly with the childContainer. I get an exception "Sequence contains more than one element" as my class is registered twice as a singleton :
ServiceTesterViewModel mainViewModel = ModuleContainer.GetInstance<ServiceTesterViewModel>();
Anyone could tell me if it is a proper solution or give me some pointers to what should be done instead.
I have a class library for caching ( Redis ), we have a unity container inside this Redis class library
public class TCache<T>
{
static readonly IUnityContainer container = new UnityContainer();
private ITCache<T> ICacheStore;
static TCache()
{
container.RegisterType<ITCache<T>, TRedisCacheStore<T>>(new ContainerControlledLifetimeManager());
}
public TCache()
{
ICacheStore = container.Resolve<TRedisCacheStore<T>>();
}
Now my senior said me not use a separate container like this and I should be using the container which is already created inside the web app with the reason being that there should be only one single container.
My question is: is it possible to access a unity container that resides in a different project and is it necessary to do this change ?
Note: I cannot add the reference of the web app into the Redis cache class library.
You should only reference a container within your composition root (http://blog.ploeh.dk/2011/07/28/CompositionRoot/).
In other words, find where your current services are registered, and perform the generic registration there.
Your type that requires a cache store then takes your abstraction via constructor injection:
public class Cache<T>
{
private readonly ITCache<T> cacheStore;
public Cache(ITCache<T> cacheStore)
{
this.cacheStore = cacheStore
?? throw new ArgumentNullException(nameof(cacheStore));
}
}
By the way, using T as a prefix for your types (rather than as a prefix for generic type parameters) is very confusing.
The names TCache and ITCache are also very confusing.
Well, I share his view of the usage of the container. How I fixed this issue is (without going into the details of how I actually created it):
Make an option to register onto the container through an interface. Something like IRegisterContainer.Register(IUnityContainer container).
Then at the moment where you now register the mappings to the container, you extend that function to also search your assembly for all objects that implement that IRegisterContainer and make them register themselves.
And use this as a platform to fix your problem.
If you want to use the IUnityContainer in your TCache object to resolve the TRediscacheStore. Simply let the IUnityContainer register itself.
container.Register<IUnityContainer, container>().
And make it a dependency in the constructor of TCache.
I've been working with Caliburn.Micro and MEF and I'm trying to get a viewmodelfactory implementation working. What I'm attempting is to create a chain of ViewModels for a dialog window (each ViewModel instantiates one or more viewmodels to generate the overall layout of the window). I'm having trouble importing the viewmodelfactory correctly; I can get it without a problem using [ImportingConstructor], however when I try to use [import] I end up with a null reference exception against viewModelFactory.
The "ViewModelFactory" which I have implemented is as per:
http://blog.pglazkov.com/2011/04/mvvm-with-mef-viewmodelfactory.html
and I'm trying to import the viewmodel as per the following:
[Import]
public IViewModelFactory viewModelFactory { get; set; }
and IViewModelFactory itself has an export declared (and works correctly with [ImportingConstructor]
[Export(typeof(IViewModelFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ViewModelFactory : IViewModelFactory
Attempt 2
MY next effort was trying to add an instance of ViewModelFactory into the composition container:
protected override void Configure()
{
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue<IViewModelFactory>(new ViewModelFactory());
batch.AddExportedValue(container);
container.Compose(batch);
}
However this results in an error within the ViewModelFactory, stating that the composition container which is Lazy loaded is null.
I'm trying to find a solution that will allow me to still use the Factory approach, as it allows me to use constructor parameters which are currently required as part of my viewmodels.
EDIT
I was able to get this to work by having an "Initialise" function within my viewmodels, using [ImportingConstructor] on my ViewModels with a constructor that only contains the IViewModelFactory declaration. However, this now required me to instantiate the viewmodel and make a call to the "initialise" function whenever I am creating these, so a more elegant approach would be great.
Thanks.
Managed to implement a different solution to this, which was to use:
IoC.Get<*ViewModelName*>();
Still haven't worked out why the [Import] by itself didn't work, however this certainly solved the issue for me.
As I understand it, to have a single resolve call all entities must be "linked together" through their dependencies. When resolving the root entity the DI container will recursively create the rest.
In the samples I have seen for Prism the Shell window has no explicit depencies so when it is resolved in the CreateShell of the bootstrapper it all stops there. Because of this modules must be explicitly resolved in the InitializeModules method.
Likewise, inserting views into the shell is usually done by resolving them in the module Initialize method and explicitly setting them to a given region, thus using the DI container more as a service locator.
Does anybody know how to link things together to enable a single resolve in Prism?
There is an application which has two modules Module1 and Module2.
You are saying the following is
public ShellView(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator, IModule1 mod1, IModuel2 mod2)
{
}
better than this
public ShellView(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator)
{
}
public class IModule1 : IModule
{
public void Initialize()
{
var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("Region1", typeof(Module1View));
}
}
public class IModule2 : IModule
{
public void Initialize()
{
var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("Region2", typeof(Module2View));
}
}
Which way one could do the project depends on many factors. For example I can selct the second approach citing
Modularity
The second approach is more loose coupled than the first. The first approach forces the app to declare the modules it is going to use.
I can decide my app will only define regions and I will create modules later on and inject the views to the respective regions.
I am not saying this is absolutely the case as you can very well select the first approach saying as the modules itself won't have any reference about where it is going to be used that approach is more modular. All I am saying is there are options about how to go about a problem and limiting the options is generally not a good idea.
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
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.
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.
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.
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 ^_^