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.
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 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.
I just started with Caliburn.Micro.
I'm trying to bootstrap my simple sample solution placing the ShellView (usercontrol) in an Test.App assembly, and the ShellViewModel in the Test.ViewModel assembly.
What i get is a window with the following text: "Cannot find view for Caliburn.Test.ViewModel.ShellViewModel".
But if I move the ViewModel to the .App assembly, it works perfectly.
this is the Bootstraper in the Caliburn.Micro.Test assembly (executable):
namespace Caliburn.Micro.Test
{
public class AppBootstrapper : BootstrapperBase
{
SimpleContainer container;
public AppBootstrapper()
{
this.Start();
}
protected override void Configure()
{
container = new SimpleContainer();
this.container.Singleton<IWindowManager, WindowManager>();
this.container.Singleton<IEventAggregator, EventAggregator>();
this.container.PerRequest<IShell, ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = this.container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return this.container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
this.container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
this.DisplayRootViewFor<IShell>();
}
protected override IEnumerable<System.Reflection.Assembly> SelectAssemblies()
{
var assemblies = new List<Assembly>()
{
Assembly.GetExecutingAssembly(),
Assembly.Load("Caliburn.Micro.Test.ViewModel"),
};
return assemblies;
}
}
}
this is my ViewModel in the Caliburn.Micro.Test.ViewModel assembly (class library):
namespace Caliburn.Micro.Test.ViewModel
{
public interface IShell
{
}
public class ShellViewModel : IShell
{
}
}
Can you help me solve my problem, please?
Thank you! :D
Check that you have selected your assembly for CM by overriding SelectAssemblies in your bootstrapper.
The documentation here has an example:
http://caliburnmicro.codeplex.com/wikipage?title=Customizing%20The%20Bootstrapper
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[] {
Assembly.GetExecutingAssembly()
};
}
Edit:
Ok not only do you need to select assemblies to tell CM where to look - it sounds like in your case your VMs and your Views may be in different namespaces since you have them in separate libraries. You can use the same root namespace in both libraries and the standard view resolution should work fine - however, you need to make sure you have selected the assembly in the bootstrapper in order to tell CM what assemblies to try to resolve views in.
If you want to put your views/VMs in different namespaces for some reason or another, you need to customise the logic that CM uses to resolve a view. It uses naming conventions to locate a View based on the fully qualified type name of the viewmodel (or vice-versa if you are using a view-first approach)
I suggest reading up on the introductory documentation:
http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation
Then follow it through. If you want to skip directly to naming conventions, check out this particular page:
http://caliburnmicro.codeplex.com/wikipage?title=View%2fViewModel%20Naming%20Conventions&referringTitle=Documentation
and
http://caliburnmicro.codeplex.com/wikipage?title=Handling%20Custom%20Conventions&referringTitle=Documentation
Solved thanks to this article
http://www.jerriepelser.com/blog/split-views-and-viewmodels-in-caliburn-micro/
EDIT: since you integrated your reply with mine I change the accepted answer to be yours.
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());
}
}