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.
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'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".
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'm just trying to get up off the ground and get used to working with Prism, in my bootstrapper I have:
public class Bootstrapper : UnityBootstrapper
{
private readonly EnterpriseLibraryLoggerAdapter _logger = new EnterpriseLibraryLoggerAdapter();
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<Shell>();
}
protected override ILoggerFacade CreateLogger()
{
return _logger;
}
for my App OnStartup:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
And the logging adapter
public class EnterpriseLibraryLoggerAdapter : ILoggerFacade
{
#region ILoggerFacade Members
public void Log(string message, Category category, Priority priority)
{
Logger.Write(message, category.ToString(), (int)priority); // <--Blows up here
}
#endregion
}
When the bootstrapper Runs, it hits the Logger.Write and throws an exception:
The type LogWriter does not have an accessible constructor.
I'm following from the StockTraderRI sample app. Am I missing a registration somewhere?
moved the configuration to my bootstrapper constructor and things seem to be working
var builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging()
.WithOptions
.DoNotRevertImpersonation()
.LogToCategoryNamed("Debug")
.SendTo.FlatFile("Basic Log File")
.FormatWith(new FormatterBuilder()
.TextFormatterNamed("Text Formatter")
.UsingTemplate(
"Timestamp: {timestamp}{newline}Message: {message}{newline}Category: {category}{newline}"))
.ToFile("core.log")
.SendTo.RollingFile("Rolling Log files")
.RollAfterSize(1024)
.ToFile("RollingTest.log")
.LogToCategoryNamed("General")
.WithOptions.SetAsDefaultCategory()
.SendTo.SharedListenerNamed("Basic Log File");
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);