I am writing a WPF application using PRISM 4.1 & Unity. The application will have every UI component configurable, including the Shell itself!
E.g. I have an interface called IShell. Consumers of the app can have their own implementation of IShell if they are not satisfied with my default implementation which has Regions and Views defined in some fixed way.
Now from my Bootstrapper class (which inherits UnityBootstrapper), I want to know the type registered for IShell with the Unity container. The overriden CreateShell will return configured type of the IShell.
My App.config looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<container>
<register type="Interfaces.IShell, Interfaces" mapTo="PrismApp.Shell, PrismApp"/>
</container>
</unity>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
In Bootstrapper class, I have following code:
public class PrismAppBootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
var obj = ServiceLocator.Current.GetInstance<IShell>() as DependencyObject;
return obj;
}
}
In the WPF application's App.xaml.cs, I am instantiating PrismAppBootstrapper:
PrismAppBootstrapper prismAppBootstrapper = new PrismAppBootstrapper();
prismAppBootstrapper.Run();
However, when I run this code, I get the Exception : "InvalidOperationException - The current type, Interfaces.IShell, is an interface and cannot be constructed. Are you missing a type mapping?"
How to solve this problem?
Why does application fail to know the registered type of IShell which is there in app.config file?
Keeping the Shell configurable is possible with the Unity container.
The problem was that my Unity Container was not properly configured to read mapping/registrations from the app.config file. So it failed to know the mapping of IShell at run time.
I had to override other methods along with CreateShell in my PrismAppBootstrapper class.
E.g.
public class PrismAppBootstrapper : UnityBootstrapper
{
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog catalog = new ConfigurationModuleCatalog();
return catalog;
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
UnityConfigurationSection configurationSection =
(UnityConfigurationSection) ConfigurationManager.GetSection("unity");
if (configurationSection != null)
{
configurationSection.Configure(this.Container);
}
}
protected override DependencyObject CreateShell()
{
IShell shell = this.Container.TryResolve<IShell>();
return shell as Window;
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)this.Shell;
Application.Current.MainWindow.Show();
}
}
Shouldnt you instead use the UnityBootstrapper's Container to resolve your shell?
var obj = Container.Resolve<IShell>() as DependencyObject;
return obj;
Related
hi i want when application luanched, application navigate to a view automatically so i used like this:
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainContent>();
containerRegistry.RegisterForNavigation<Subscene>();
}
and in MainWindowViewModel
public MainWindowViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
_regionManager.RequestNavigate("ContentRegion", "Subscene", myparameter);
}
this code not work, No errors and no navigation
my region exist in another usercontrol called MainContent
This works for "normal" view models only, not for the view model of the shell, because that one is created too early. You should do the initial navigation in OnInitialized.
internal class MyApp : PrismApplication
{
// ...
protected override void OnInitialized()
{
base.OnInitialized();
Container.Resolve<IRegionManager>().RequestNavigate("ContentRegion", "Subscene", myparameter);
}
// ...
}
Unrelated sidenote: if you use the parameter instead of the field in the constructor, Resharper will tell you when the field is no longer needed in future refactorings.
I am trying to create a very basic application using Prism, MEF and WPF. I have a WPF application project which has a Shell.xaml and Bootstrapper. Code for BootStrapper is below:
public class SimpleMefApplicationBootstrapper : MefBootstrapper
{
protected override void ConfigureAggregateCatalog()
{
//base.ConfigureAggregateCatalog();
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(SimpleMefApplicationBootstrapper).Assembly));
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(PrismApp.Module.Hello.HelloModule).Assembly));
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(PrismApp.Module.Hello.FinishModule).Assembly));
}
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
//base.InitializeShell();
Application.Current.MainWindow = (Window)this.Shell;
}
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var factory = base.ConfigureDefaultRegionBehaviors();
return factory;
}
In the solution i have another class library which has a View folder, View model Folder and two modules. The two modules are tied to two views so that they can used as a region. It works perfectly if i try to call only one module from botstrapper but not when i call both the modules. It gives me the error details like :
A duplicate module with the name FinishModule has been found by the Loader.
I dont understand if both the modules are having different then what is the problem. I tried changing the assemble also for both the modules but no luck.
Any ideas?
Try using only one call:
public class SimpleMefApplicationBootstrapper : MefBootstrapper
{
protected override void ConfigureAggregateCatalog()
{
//base.ConfigureAggregateCatalog();
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(SimpleMefApplicationBootstrapper).Assembly));
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(PrismApp.Module.Hello.HelloModule).Assembly));
// this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(PrismApp.Module.Hello.FinishModule).Assembly));
}
I am using Prism 4 with Unity Extensions and the MVVM pattern.
public class Bootstrapper : UnityBootstrapper {
protected override void InitializeShell() {
Application.Current.RootVisual = (UIElement) Shell;
}
protected override DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance<Shell>();
}
}
ServiceLocator.Current.GetInstance() throws the following exception :
Activation error occured while trying to get instance of type Shell,
key ""
You probably have an error in the shell constructor.
If you have parameters in Shell constructor for exemple, dont forget to declare in ConfigureContainer of Bootstrapper :
Bootstrapper.cs
...
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.Container.RegisterType<IMyService, MyService>(new ContainerControlledLifetimeManager());
}
Shell.cs
...
public Shell(IMyService container)
{
...
}
You can read this doc : http://msdn.microsoft.com/en-us/library/gg430868(v=pandp.40).aspx
I have two seperate suggestions:
1)ensure that the name property of shell.xaml is not empty
2)If 1 does not help change the InitializeShell like this:
IShellView shell = new Shell();
shell.ShowView();
return shell as DependencyObject;
I hope these could help.
I've been trying to write a WPF application using PRISM and MEF and I've been able to get the Shell up and running. I want to be able to load Modules on demand so I need an instance of IModuleManager in the Shell. However, when I try to import it, the application breaks. Here is the relevant code:
Bootstrapper:
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
// Add this assembly to export ModuleTracker
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
moduleCatalog.Load();
foreach (ModuleInfo moduleInfo in moduleCatalog.Modules)
{
this.ModuleCatalog.AddModule(moduleInfo);
}
DirectoryCatalog catalog = new DirectoryCatalog(#".\Modules");
this.AggregateCatalog.Catalogs.Add(catalog);
base.ConfigureAggregateCatalog();
}
protected override void ConfigureContainer()
{
//Export the Container so that it can be injected if needed.
this.Container.ComposeExportedValue(Container);
//Export the Module Catalog so that it can be injected if needed.
this.Container.ComposeExportedValue(ModuleCatalog);
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
Shell:
[Export(typeof(Shell))]
public partial class Shell : Window, IPartImportsSatisfiedNotification
{
[Import(AllowRecomposition = false)]
private IModuleManager moduleManager;
public Shell()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
public void OnImportsSatisfied()
{
}
}
The exception I'm getting is:
No exports were found that match the constraint:
ContractName Shell
RequiredTypeIdentity Shell
If I remove the [Import] attribute for the IModuleManager, everything works fine. Is there something I need to do to export IModuleManager?
Resolved this issue by commenting the following line in the Bootstrapper:
this.Container.ComposeExportedValue(ModuleCatalog);
Not sure why it was causing an issue but would welcome any insights into this matter.
One thing that you have to watch out for is having multiple exports for the same type. This can easily happen when you are using ComposeExportedValue as well as using a DirectoryCatalog (which could contain an Export of the same type).
There is a great package on Nuget for diagnosing these problems called MEFX
If you get this library, you can add the following lines to help find out what is going on
var compositionInfo = new CompositionInfo(AggregateCatalog, Container);
CompositionInfoTextFormatter.Write(compositionInfo, Console.Out);
I would be interested to see what this prints to the Output window when you leave this.Container.ComposeExportedValue(ModuleCatalog); in your program, if you wouldn't mind posting any errors from it.
Edit:
I have found one method to do this but I'm not sure if it is the best way.
In WindsorContainer initialization, first I register viewmodel:
container.Register(Component.For<CentrosViewModel>().LifeStyle.Transient);
and later I register the View
container.Register(Component.For<CentrosAdminView>().LifeStyle.Transient.DependsOn(Property.ForKey("DataContext")
.Eq(ViewModelLocator.Centrosviewmodel)));
And definition of property ViewModelLocator.Centrosviewmodel is:
public static CentrosModel Centrosviewmodel
{
get
{
return App.container.Resolve<CentrosViewModel>();
}
}
End Edit
I'm trying to make an Wpf application using Castle Windsor and Mvvm Toolkit (galasoft) but I thing my problem will be the same with any MVVM toolkit.
With MVVM you must set the DataContext of the View to your ViewModel. Normally this is done by something like this in declaration of the view
DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}}
Resource Locator is defined in App.xaml like this:
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator" />
</Application.Resources>
If I establish StartupURI in App.xaml to my view all is right.
But if I leave StartupUri blank and I try to get an instance of my view through castle using following syntax:
container.Resolve<CentrosAdminView>().Show();
I get exception: "Cannot Find Resource with Name '{Locator}'
I supose that Initial DataContext is different when running directly than when running through Castle Windsor and this is the reason why it can't find resource.
My two questions are:
Is It necessary have a ViewModelLocator when using Castle Windsor? In
case of Yes: How can I setup DataContext of Views correctly with
Windsor? In case of No: How would be the right way?
I leave down my Castle Configuration. Any help would be really appreciated.
My Windsor configuration look like this:
<castle>
<properties>
<!-- SQL Server settings -->
<connectionString>Server=192.168.69.69;Database=GIOFACTMVVM;user id=sa;password=22336655</connectionString>
<nhibernateDriver>NHibernate.Driver.SqlClientDriver</nhibernateDriver>
<nhibernateDialect>NHibernate.Dialect.MsSql2005Dialect</nhibernateDialect>
</properties>
<facilities>
<facility id="nhibernatefacility"
type="Repository.Infrastructure.ContextualNHibernateFacility, Repository">
<factory id="sessionFactory1">
<settings>
<item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="connection.driver_class">#{nhibernateDriver}</item>
<item key="connection.connection_string">#{connectionString}</item>
<item key="dialect">#{nhibernateDialect}</item>
<item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
</settings>
<assemblies>
<assembly>Domain</assembly>
<assembly>ObservableCollections</assembly>
</assemblies>
</factory>
</facility>
</facilities>
</castle>
and by code:
public static IWindsorContainer Start()
{
var container = new WindsorContainer(new XmlInterpreter());
container.AddFacility<TransactionFacility>();
container.Register(
Component.For<HistoriasAdminView>().LifeStyle.Transient,
Component.For<HistoriasModel>().LifeStyle.Transient,
Component.For<CentrosModel>().LifeStyle.Transient,
Component.For<CentrosAdminView>().LifeStyle.Transient,
Component.For<MainViewModel>().LifeStyle.Transient,
Component.For<MainWindow>().LifeStyle.Transient,
Component.For<IRepository<Historias>>().ImplementedBy<Repository<Historias>>().LifeStyle.Transient,
Component.For<IRepository<Centros>>().ImplementedBy<Repository<Centros>>().LifeStyle.Transient,
Component.For<IUnitOfWork>().ImplementedBy<NHUnitOfWork>().LifeStyle.Transient
);
return container;
}
You are using the Service Locator pattern, where you register services, pass around a reference to the container, and explicitly call resolve all over you code. If you take a quick tour through the Castle Windsor wiki, they discourage this use of the container.
Generally, you should register all your types (via installers), resolve only one root object (maybe your main view, maybe some sort of startup/MVC controller style code), and let the rest be resolved by the container. The next time you will call the container will almost always be container.Dispose, when your application exits.
See the Windsor wiki page regarding the Three Calls Pattern.
If you find cases where you need to pull from the container during runtime to create specific instances (where you have to pass specific parameters to create that instance), use the Typed Factory Facility instead of directly resolving dependencies.
MVVM with Windsor:
In my first MVVM app I wrote with Windsor (just finished the first version), I simply registered my main view and view model without specifying the lifestyle. This defaulted them to be singletons.
The view took a view model instance as a required dependency (in the constructor), and used it to set the data context. I did this in code-behind because it was non-intrusive and painless.
// In my program I used interfaces for everything. You don't actually have to...
public interface IMainView
{
void Show();
}
public class MainView : Window, IMainView
{
public MainView(IMainViewModel viewModel)
{
Initialize();
this.DataContext = viewModel;
}
}
public interface IMainViewModel
{
int SomeProperty { get; set; }
ICommand ShowSubViewCommand { get; }
// ...
}
public class MainViewModel : IMainViewModel
{
public MainViewModel(SomeOtherSubComponent subComponent)
{
this.subComponent = subComponent;
// ...
}
// ...
}
Where I had sub-views that I wanted to create multiple instances of, I registered them with a transient life-cycle. Then I created a view factory and view model factory, and used them to get instances of the sub-view and sub-viewmodel from the parent view. I registered an event handler for the view's close event, and called a Release method on the factory classes.
public interface ISubView
{
void Show();
event Action OnDismissed;
}
public class SubView : Window, ISubView
{
public SubView(ISubViewModel viewModel)
{
Initialize();
this.DataContext = viewModel;
// Would do this in the view model,
// but it is a pain to get Window.Close to call an ICommand, ala MVVM
this.OnClose += (s, a) => RaiseDismissed();
}
public event Action OnDismissed;
private void RaiseDismissed()
{
if(OnDismissed != null)
OnDismissed();
}
}
public interface ISubViewModel
{
string SomeProperty { get; }
// ...
}
// Need to create instances on the fly, so using Typed Factory facility.
// The facility implements them, so we don't have to :)
public interface IViewFactory
{
ISubView GetSubView(ISubViewModel viewModel);
void Release(ISubView view);
}
public interface IViewModelFactory
{
ISubViewModel GetSubViewModel();
void Release(ISubViewModel viewModel);
}
// Editing the earlier class for an example...
public class MainViewModel : IMainViewModel
{
public MainViewModel(IViewFactory viewFactory, IViewModelFactory viewModelFactory)
{
this.viewFactory = viewFactory;
this.viewModelFactory = viewModelFactory;
// Todo: Wire up ShowSubViewCommand to call CreateSubView here in ctor
}
public ICommand ShowSubViewCommand { get; private set; }
private void CreateSubView()
{
var viewModel = viewModelFactory.GetSubViewModel();
var view = viewFactory.GetSubView(viewModel);
view.OnDismissed += () =>
{
viewModelFactory.Release(viewModel);
viewFactory.Release(view);
};
view.Show();
}
// Other code, private state, etc...
}
At the end of all this, the only calls to the container are:
void App_Startup()
{
this.container = new WindsorContainer();
container.Install(Configuration.FromAppConfig());
var mainView = container.Resolve<IMainView>();
mainView.Show();
}
public override OnExit()
{
container.Dispose();
}
The benefit to all this rigmarole is that it is independent of the container (and can be used without a container), it is obvious which dependencies each of my components take, and most of my code doesn't ever have to ask for its dependencies. The dependencies are just handed to each component as it needs it.