I was looking a WindowManager for UnityContainer and I register in my container this service.
But at the momento when I plan to utilize this service to show a UserControl, it's not possible. It tells that it can't find a view for the view model 'x'.
I'm using Unity as the bootstrapper.
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
MainWindow shell = Container.Resolve<MainWindow>();
shell.Show();
return shell;
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterInstance<IWindowManager>(new WindowManager());
}
}
Related
I am trying to understand/learn Prism with Unity
I created following classes:
==========================================
Seperate Assembly containing a "Module":
using GlobalContracts;
using Prism.Ioc;
using Prism.Modularity;
namespace ModuleA
{
[Module(ModuleName = MyModuleA.NAME, OnDemand = true)]
public class MyModuleA : IModule
{
public const string NAME = "MyModuleA";
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<MyControlA>();
containerRegistry.Register<IView, MyControlA>(NAME);
containerRegistry.Register<PluginViewModelBase, MyControlViewModel>(NAME);
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
}
}
==========================================
A ViewModel
using GlobalContracts;
namespace ModuleA
{
public class MyControlViewModel : PluginViewModelBase
{
public MyControlViewModel(IView view) : base(view)
{
}
}
}
==========================================
The Host Application (other assembly):
public partial class App : PrismApplication
{
private Shell mShell;
private ShellViewModel mShellViewModel;
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog(){ModulePath = #"..\..\..\..\ModulesOutput"};
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IView, Shell>();
containerRegistry.Register<IViewModel, ShellViewModel>();
}
protected override Window CreateShell()
{
mShellViewModel = Container.Resolve<ShellViewModel>();
mShell = (Shell)mShellViewModel.View;
return mShell;
}
(...)
Now my question is:
How do I tell Prism to resolve the IView-Parameter passed to the
constructor of the ViewModel properly?
It resolves it as "Shell" and not as "MyControlA".
Further tips regarding my code are welcome
I found some sources in the web but they used "RegisterType" method of a container. And for now I do not have dependencies to Unity in my ModuleA and I would not know how to get the container to call the "RegisterType". All sources are outdated in the web..
By default, it resolves the default registration, which in your case is Shell.
Registering a type with a name does not mean that that name is automatically used to resolve dependencies. You have to do that manually, with parameter override, injection factory or the like. But I'd try to avoid that as it makes things a bit fragile and tedious.
i'm new to Prism, i have an application and i need to have a custom region mapping.
when i try to resolve IRegionBehaviorFactory with ServiceLocator, i receive the error :
Activation error occurred while trying to get instance of type IRegionBehaviorFactory, key ""
I understand that is because the class RegionBehaviorFactory receive Microsoft.Practices.Unity.IServiceLocator, but with Unity 7.0 i use as ServiceLocator Unity.ServiceLocation.UnityServiceLocator
How can i do?
This is my bootstrapper.cs
class Bootstrapper : UnityBootstrapper
{
private UnityContainer uc = new UnityContainer();
protected override void ConfigureServiceLocator()
{
base.ConfigureServiceLocator();
UnityServiceLocator locator = new UnityServiceLocator(uc);
ServiceLocator.SetLocatorProvider(() => locator);
}
protected override DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance<wMain>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow = (wMain)this.Shell;
Application.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
protected override void InitializeModules()
{
base.InitializeModules();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
uc.RegisterType<IServiceLocator, UnityServiceLocator>();
uc.RegisterType<IRegionBehaviorFactory, RegionBehaviorFactory>();
Application.Current.Resources.Add("IoC", uc);
}
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
var mappings = base.ConfigureRegionAdapterMappings();
var aa = ServiceLocator.Current.GetInstance<IRegionBehaviorFactory>();
mappings.RegisterMapping(typeof(RadPaneGroup), new RadPaneGroupRegionAdapter(uc.Resolve<RegionBehaviorFactory>()));
return mappings;
}
}
Thanks
Why are you creating anew container and service locator? That class is all wrong. You don't need to create those as the container with all services has already been created for you. That's why you're using the UnityBootstrapper. The ServiceLocator is also already setup for you.
You should go through these samples to better understand hwo to use Prism: https://github.com/PrismLibrary/Prism-Samples-Wpf
Another bit of advice, don't use the ServiceLocator like you are. That's bad practice. Stick with common DI patterns and resolve objects usin ctor injectin or by resolving objects from your container.
I have a WPF app that uses the Prism.Wpf and Prism.Unity NuGet packages (both 6.3.0). I'm currently registering types in the Unity container manually in a bootstrapper class (see below) and everything is working great.
internal class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
// Register types
Container.RegisterType<IDialogService, DialogService>(new ContainerControlledLifetimeManager());
}
}
However, when I try to register types by convention, I get a Microsoft.Practices.Unity.DuplicateTypeMappingException when registering the types in the Unity container.
The register by convention code:
protected override void ConfigureContainer()
{
base.ConfigureContainer();
// Register types by convention
Container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
}
Exception message:
An attempt to override an existing mapping was detected for type Prism.Regions.IRegionNavigationContentLoader with name "", currently mapped to type Prism.Unity.Regions.UnityRegionNavigationContentLoader, to type Prism.Regions.RegionNavigationContentLoader.
How do I register types by convention when using Prism & Unity?
Just swap Container.RegisterTypes(...); and base.ConfigureContainer();
The UnityBootstrapper will only register types that weren't registered before, so you should be fine.
I have a Prism application where I have three modules:
SharedServiceModule(I am using SharedServices to communicate between modules)
ModuleA
ModuleB
SharedServiceModule just has interface and its implementation CommonService:
public interface ICommonService
{
string SomeStorage { get; set; }
}
public class CommonService : ICommonService
{
string fooStorage;
public string FooStorage
{
get
{
return fooStorage;
}
set
{
fooStorage = value;
OnPropertyChanged("FooStorage");
}
}
}
What I want is to create communication between modules using Shared Services. So I assign «ModuleAValue» at ViewModelA of ModuleA and then I want to read this value in ViewModelB of ModuleB. Let's see details.
I create a single instance of ICommonService in ViewModelA of ModuleA and assign a value "ModuleAValue" to FooStorage:
Method of ViewModelA:
unityContainer = new UnityContainer();
unityContainer.RegisterType<ICommonService, CommonService>(new ContainerControlledLifetimeManager());
IMyService someFoo = unityContainer.Resolve<ICommonService>();
someFoo.FooStorage = "ModuleAValue";//value is "ModuleAValue" in FooStorage
Then I want to read this data in viewModelB of ModuleB. But value of FooStorage is not 'Module A', but just empty value:
Method of ViewModelB:
IUnityContainer unityContainer=new UnityContainer//creation of UnityContainer in ModuleB
ICommonService someFoo = unityContainer.Resolve<CommonService>();
string str=someFoo.FooStorage;//value is empty in
FooStorage, but it should be "ModuleAValue"
My Bootstrapper is:
public class Bootstrapper:UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<IShellViewModel, ShellViewModel>();
RegisterTypeIfMissing(typeof(IMyService), typeof(MyService), true);
}
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
return mappings;
}
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(ModuleAModule));
catalog.AddModule(typeof(ModuleBModule));
return catalog;
}
}
What am I doing wrong? In my view, Unity always creates new instance of CommonService. What am I doing wrong while taking the same instance from Unity container?
Any help will be greatly appreciated!
Your app's bootstrapper creates an UnityContainer for you, see UnityBootstrapper.cs:
protected virtual IUnityContainer CreateContainer()
{
return new UnityContainer();
}
The bootstrapper should also register the container as viewmodel factory:
protected override void ConfigureContainer()
{
base.ConfigureContainer();
ViewModelLocationProvider.SetDefaultViewModelFactory( type => Container.Resolve( type ) );
}
In your module definition class, you can have this 'global' container injected as dependency:
public class ClientModule : IModule
{
private readonly IUnityContainer _unityContainer;
public ClientModule( IUnityContainer unityContainer )
{
_unityContainer = unityContainer;
}
}
In your module's initialization, you register types or instances with this container:
public void Initialize()
{
// Register services
_unityContainer.RegisterType<IGameClock, LocalGameClock>( new ContainerControlledLifetimeManager() );
_unityContainer.RegisterType<IWorldStateService, LocalWorldStateService>( new ContainerControlledLifetimeManager() );
}
In your views (in the xaml), you can now use ViewModelLocator.AutoWireViewModel="True" to automatically create viewmodels for your views. The ViewModelLocationProvider will use the 'global' container to resolve the viewmodels (as defined above), so all viewmodels will receive the same instance of our, say, IGameClock.
Ancilliary piece of advice: you don't want to call Resolve directly yourself. Using it this way defeats the whole purpose of using unity in the first place. It's better to have your dependencies injected as constructor parameters, and to only use the container at the highest level, i.e. in module initialization. And you should never need to create more than one container, lest you know exactly what you're doing.
You shouldn't create the new container instance. Usually there should be only one container instance in your application.
If you are using prism your view model should be created from a container also (if prism is responsible for view model creation than it is created from container ). In such case just create the constructor in your view model with parameter of type ICommonService like this:
public ViewModelA(ICommonService service) { ... }
then during creation of the ViewModel the same instance of the service will be injected to that ViewModel.
And usually common services are registered in the Shell during application startup. But if you want you can also register a service in a module, just use the same unity container that was created during application start. In the viewmodel use constructor with parameter of type IUnityContainer.
My app is an IDE add-on, so the project is a wpf user control. I want to use Prism to refector it now. When I debug the app, it threw such an exception in InitializeComponent(); of the MainView's constructor:
"ServiceLocationProvider must be set."
I also found a similar thread here: Strange exception in Prism application
but there's no solution.
This is my bootstrapper class:
class Bootstrapper : UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance<MainView>();
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog catalog = (ModuleCatalog)this.ModuleCatalog;
catalog.AddModule(typeof(ModuleInit));
}
}
This is my ModuleInit class:
public class ModuleInit : IModule
{
IRegionManager regionManager;
IUnityContainer container;
public ModuleInit(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
public void Initialize()
{
this.container.RegisterType<ViewModelA>(new ContainerControlledLifetimeManager());
this.container.RegisterType<MainViewModel>(new ContainerControlledLifetimeManager());
this.regionManager.RegisterViewWithRegion(Models.RegionNames.ARegion, () => this.container.Resolve<ViewModelA>());
}
}
Because this is a user control project, so there's no App.xaml and App.xaml.cs file. I can bootstrapper's Run method in MainView's constructor:
public MainView()
{
InitializeComponent();
Bootstrapper strapper = new Bootstrapper();
strapper.Run();
}
But the exception is thrown in the first line of the constructor. Anyone can help?
Furthermore, is there a way to use Region without modularity? My app only contains one project, it needn't modularity. If yes, where can I register the views, services and viewmodels?
Somewhere, for example in your bootstrapper before init code, you have to define your IoC container.
This is how it's done in ViewModelLocator by default:
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);