Load Prism modules from DLL - c#

Recently I took an interest in developing PRISM WPF applications. Now I'm trying to load my modules from a DLL I make after building the Modules Project (Wpf User Control Library). During the build of the modules project I copy the DLL in the debug folder (copy: xcopy /y "$(TargetPath)" "$(SolutionDir)FooBar\$(OutDir)Modules\"). Next I configure the bootstrapper and I think there is were I lost it.
I'll attach my code below.
Bootstrapper
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
var shell = ServiceLocator.Current.GetInstance<Shell>();
return shell;
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return base.CreateModuleCatalog();
}
protected override void ConfigureModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = Environment.CurrentDirectory + #"\Modules";
ModuleCatalog = moduleCatalog;
}
protected override void InitializeModules()
{
base.InitializeModules();
}
protected override ILoggerFacade CreateLogger()
{
return base.CreateLogger();
}
}
Shell.xaml.cs
protected readonly IModuleCatalog _moduleCatalog;
public Shell(IModuleCatalog moduleCatalog)
{
this._moduleCatalog = moduleCatalog;
InitializeComponent();
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanging, INotifyPropertyChanged,IModule
{
//Implementation INotify etc..
public void Initialize()
{
}
}
So I'm wondering why my ModuleCatalog.Modules is always 0. Can someone help me out ?

Is your module catalog null? or contains no module?
check that Environment.CurrentDirectory + #"\Modules"; returns the correct path and all DLL are in.
Do you really need to load them from dir? Don't you know which modules will be loaded?
I usualy do that:
public partial class Bootstrapper: UnityBootstrapper
{
protected override void ConfigureContainer()
{
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
// Create the module catalog from a XAML file.
return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("/Shell;component/ModuleCatalog.xaml", UriKind.Relative));
}
protected override DependencyObject CreateShell()
{
// Use the container to create an instance of the shell.
ShellView view = Container.TryResolve<ShellView>();
// Display the shell's root visual.
//view.ShowActivated = false;
view.Show();
return view;
}
}
and my modulecatalog
<prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<prism:ModuleInfo Ref="Connexion.dll"
ModuleName="Connexion"
ModuleType="Connexion.ModuleInit, Connexion, Version=1.0.0.0" />
<prism:ModuleInfo Ref="Tools.dll"
ModuleName="Tools"
ModuleType="Tools.ModuleInit, Tools, Version=1.0.0.0" />
<prism:ModuleInfo Ref="DrawingModule.dll"
ModuleName="DrawingModule"
ModuleType="DrawingModule.ModuleInit, DrawingModule, Version=1.0.0.0"
InitializationMode="WhenAvailable">
<prism:ModuleInfo.DependsOn>
<sys:String>Connexion</sys:String>
<sys:String>Tools</sys:String>
</prism:ModuleInfo.DependsOn>
</prism:ModuleInfo>
<prism:ModuleInfo Ref="Sis.dll"
ModuleName="Sis"
ModuleType="Sis.ModuleInit, Sis, Version=1.0.0.0"
InitializationMode="WhenAvailable">
<prism:ModuleInfo.DependsOn>
<sys:String>Connexion</sys:String>
<sys:String>Tools</sys:String>
<sys:String>DrawingModule</sys:String>
</prism:ModuleInfo.DependsOn>
</prism:ModuleInfo>
and all modules have the buildAction: copy "$(TargetPath)" "$(SolutionDir)Shell\$(OutDir)"

Related

Prism RequestNavigate from PrismApplication immediately on start

In Prism 7 I can RegisterForNavigation and RequestNavigate from IModule like this:
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate("ContentRegion", "PersonList");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PersonList>();
}
}
and I know that I can RegisterForNavigation from PrismApplication like this:
public partial class App : PrismApplication
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
but how can I RequestNavigate from PrismApplication immediately on start?
I have tried this:
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
but this.regions.Count is 0 in RegionManager from Prism
private IRegion GetRegionByName(string regionName)
{
return this.regions.FirstOrDefault(r => r.Name == regionName);
}
"ContentRegion" definitely exists, because it works if I try from the IModule and I know that RegisterTypes from PrismApplication executes before the MainWindowViewModel constructor.
I don't know what I am missing and I can't find the answer in any examples or tutorials.
Thank you for your help!
Your best bet is to override OnInitialized in your application and do the navigation there. You can access the container to fetch the region manager through the Container property.
If you use a bootstrapper, you can override InitializeModules and navigate there.

Caliburn.Micro WPF: IoC.Get Returns Null

My code looks like this:
Bootstrapper.cs
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
_container.RegisterPerRequest(typeof(ShellViewModel), null, typeof(ShellViewModel));
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
And my ShellViewModel looks like this:
ShellViewModel.cs
public class ShellViewModel : Conductor<Screen>
{
public ShellViewModel
{
var aViewModel = IoC.Get<AViewModel>();
ActivateItem(aViewModel);
}
}
But whenever I run the program, a blank screen is shown. When I debug it, it said that the aViewModel is null.
Is there anything wrong with the Bootstrapper?
Based on the code provided, AViewModel is not registered with the container in the Bootstrapper so IoC does not know it exists, thus it will return null when requested to Get that type
For example
_container.RegisterPerRequest(typeof(AViewModel), null, typeof(AViewModel));
All types that need to be resolved by IoC should first be registered with the backing container.

Caliburn Micro Model executes, window doesn't show up

I'm encountering difficulties using Caliburn.Micro in my wpf project: any view's model's code is executed, but the window never shows up. Thus, the OnViewLoaded can show a MessageBox while the window remains invisible.
This is my bootstrapper:
public class Bootstrapper : BootstrapperBase
{
/// <summary>
/// IoC container for dependency injection
/// </summary>
private readonly SimpleContainer _container = new SimpleContainer();
private static Bootstrapper bootstrapper;
public Bootstrapper()
{
Initialize();
ConventionManager.AddElementConvention<PasswordBox>(ZBMS.PasswordHelper.BoundPasswordBox.BoundPasswordProperty, "Password",
"PasswordChanged");
}
protected override void Configure()
{
//Callback for redirecting pressed keys to the corresponding viewmodel
MessageBinder.SpecialValues.Add("$pressedkey", (context) =>
{
var keyArgs = context.EventArgs as KeyEventArgs;
return keyArgs?.Key;
});
// reset settings when debugger is attached (vs obviously)
if (Debugger.IsAttached)
Settings.Default.Reset();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
//_Container.Singleton<ISettingsManager, NETSettingsManager>();
_container.PerRequest<LoadingScreenViewModel>();
}
protected override void OnStartup(object sender, StartupEventArgs e)
=> DisplayRootViewFor<LoadingScreenViewModel>();
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[]
{
Assembly.GetExecutingAssembly()
};
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}

How to Navigate to a user control on load?

I want to choose which user control to load but my MainWindowView is not even loaded yet so the region manager does not know any regions, How can I achieve this?
my bootstrapper looks like this:
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<MainWindowView>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.Container.RegisterTypeForNavigation<WorkTypeSelectionView>();
}
and my viewmodel:
public MainWindowViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
{
this.eventAggregator = eventAggregator;
this.regionManager = regionManager;
this.AuthenticateUser();
if (this.LoggedUser.AvailableWorkTypes.Count > 1)
{
this.Navigate(nameof(WorkTypeSelectionView));
}
}
private void Navigate(string obj)
{
this.regionManager.RequestNavigate(DefaultContentRegion, obj);
}
thanks in advance!
EDIT:
Guess i was asking the wrong question, found this https://stackoverflow.com/a/7887936/171136 still want to explore other options. Thanks!
You can use View Discovery with regionManager.RegisterViewWithRegion("RegionName", typeof(View));. When the region is created, it will automatically inject the view.

PRISM navigate to another View

I have a MainWindow which only contains the region for displaying other Views:
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>
What I am trying to do, is to immediately when my MainWindowViewModel loads, to navigate to MainPageViewModel.
I have tried to implement interface INavigationAware such as following:
public void OnNavigatedTo(NavigationContext navigationContext)
{
_regionManager.RequestNavigate("ContentRegion", App.Experiences.DetailPage.ToString());
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
_regionManager.RequestNavigate("ContentRegion", App.Experiences.DetailPage.ToString());
}
But even when I set my breakpoints over these, they are never executed.
What am I doing wrong?
EDIT
Maybe I need to change my Bootstrapper logic? Here is how it looks like:
public class Bootstrapper: UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
return Container.TryResolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType(typeof(IDataRepository), typeof(DataRepository), null,new Microsoft.Practices.Unity.ContainerControlledLifetimeManager());
Container.RegisterTypeForNavigation<MainPage>(App.Experiences.MainPage.ToString());
Container.RegisterTypeForNavigation<DetailPage>(App.Experiences.DetailPage.ToString());
}
}
I had to edit my Bootstrapper logic such as following:
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
Prism.Regions.IRegionManager newRegion = Container.TryResolve<Prism.Regions.IRegionManager>();
newRegion.RequestNavigate("ContentRegion", App.Experiences.MainPage.ToString());
}

Categories

Resources