Showing Prism modules inside Prism Module - c#

I am implementing application in PRISM, which need to import modules dynamically from dll files.
I managed to do that - they are importing, but I can't display it.
I decided to create a special module to encapsulate it - let us call it ModuleDock.
So we have something like that:
Bootstrapper.cs:
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
var modules = new DirectoryModuleCatalog
{
LoadSubdirectories = true,
ModulePath = #"C:\Modules"
};
modules.AddModule(typeof(ModuleDockModule));
return modules;
}
}
Shell.xaml:
<ContentControl regions:RegionManager.RegionName="ModuleDockModule" />
ModuleDockModule.cs (in ModuleDock project):
public class ModuleDockModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _unityContainer;
public void Initialize()
{
RegisterIoc();
if (_regionManager.Regions.ContainsRegionWithName("ModuleDockModule"))
{
_regionManager.RegisterViewWithRegion("ModuleDockModule", typeof(ModuleDockView));
}
}
public ModuleDockModule(IRegionManager regionManager, IUnityContainer unityContainer, IRegionViewRegistry regionViewRegistry)
{
_regionManager = regionManager;
_unityContainer = unityContainer;
}
private void RegisterIoc()
{
_unityContainer.RegisterType<IModuleDockView, ModuleDockView>();
_unityContainer.RegisterType<IModuleDockViewModel, ModuleDockViewModel>();
}
}
and finally in one of loaded modules:
[Module(ModuleName = "TestModule", OnDemand = false)]
public class TestModuleModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _unityContainer;
public void Initialize()
{
RegisterIoc();
if (_regionManager.Regions.ContainsRegionWithName("TestModule"))
{
_regionManager.RegisterViewWithRegion("TestModule", typeof(TestView));
}
}
public TestModuleModule(IRegionManager regionManager, IUnityContainer unityContainer)
{
_regionManager = regionManager;
_unityContainer = unityContainer;
}
private void RegisterIoc()
{
_unityContainer.RegisterType<ITestView, TestView>();
_unityContainer.RegisterType<ITestViewModel, TestViewModel>();
}
}
For test purposes I've created that line of XAML:
<ContentControl regions:RegionManager.RegionName="TestModule" />
Could you tell me, why that line displays TestModule in Shell.xaml, but don't display it in ModuleDockView.xaml?
Please, mind that in final stage I have to use various number of unknown modules provided by other users of my platform, so I can't make anything static (like module names or initializations).
Thank you in advance!

Based on the code you described, the TestModule module has a module dependency with ModuleDockModule as this is defining the region that the TestModule's Views would then be registered into.
Therefore, you would need to make sure that ModuleDockModule is initialized before TestModule and any other module that would depend on it. In order to do this, you would need to declare a dependency attribute on TestModuleModule class, right above the class definition as follows:
[Module(ModuleName = "TestModule", OnDemand = false)]
[ModuleDependency("ModuleDockModule")]
public class TestModuleModule : IModule
{ ...
For more information about Moduled you may refer to the following MSDN Prism Guide chapter:
Modular Application Development
I hope this helped you, Regards.

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.

Switch View in different Modules using Prism and Unity

I want to navigate from ModuleA View to ModuleB View. How can I implement navigation between modules?
In my application which uses Prism framework, I have two module
ModuleA
ModuleB
I configure two modules in my Bootstrapper like this:
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(ModuleA));
moduleCatalog.AddModule(typeof(ModuleB));
}
I register my both Views in my ModuleA and ModuleB like this:
public class ModuleA : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
public StaffModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
public void Initialize()
{
this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<StaffView>());
}
}
public class AccountModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
public AccountModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
public void Initialize()
{
container.RegisterType<object, AccountView>("AccountView");
////this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<AccountView>());
}
}
When I click Button from StaffView in ModuleA, I want to navigate to AccountView in ModuleB. Here is my code for navigation.
private void LodeViewfromModule()
{
IUnityContainer unityContainer = ServiceLocator.Current.GetInstance<IUnityContainer>();
var regionManager=unityContainer.Resolve<IRegionManager>();
var uri = new Uri("pack://application:,,,/PrismAuto.Account;component/AccountView.xaml", UriKind.RelativeOrAbsolute);
regionManager.RequestNavigate("MainRegion", uri);
}
But it shows:
System.Object exception.
Please, anyone help me to solve this problem.
You are registering your view for navigation using:
container.RegisterType<object, AccountView>("AccountView");
and navigating to it like:
var uri = new Uri("pack://application:,,,/PrismAuto.Account;component/AccountView.xaml", UriKind.RelativeOrAbsolute);
regionManager.RequestNavigate("MainRegion", uri);
This is wrong. You need to navigate to it using the key your provided when you registered it for navigation:
regionManager.RequestNavigate("MainRegion", "AccountView");
Also, if you are using Prism 6 there is an extension method in the Prism.Unity namespace for registering your views for navigation like this:
container.RegisterTypeForNavigation<AccountView>();

How to utilise Event aggregator in MEF?

I am running the latest PRISM 4.2. Unfortunately the Event Aggregator tutorial in the documentation is driven via Unity instead of MEF. And I can't get it running under MEF.
App.xaml.cs
public partial class App : Application
{
[Import]
public IEventAggregator eventAggregator;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
Bootstrapper.cs
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return new MainWindow();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
}
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog moduleCatalog = new ModuleCatalog();
return moduleCatalog;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window, IView
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
MainViewModel.cs:
[ModuleExport(typeof(MainViewModel))]
public class MainViewModel : BindableBase, IServiceCallback
{
[Import]
public IEventAggregator eventAggregator;
[ImportingConstructor]
public MainViewModel()
{
eventAggregator.GetEvent<AppExitEvent>().Publish("");
}
}
Despite the [import] the event aggregator is always null both in App.xaml.cs and in MainViewModel. Why is that?
The second question is do I have to export my Viewmodels as a module (as I did above) to use an even aggregator inside them?
UPDATE:
Proof that latest version of PRISM doesn't support ComposeExportedValue anymore.
'System.ComponentModel.Composition.Hosting.CompositionContainer' does
not contain a definition for 'ComposeExportedValue' and no extension
method ...
The solution to this would be what SchubertJ replied on your same question at CodePlex:
How to utilise Event aggregator in MEF?
As a deeper analyisis, the Import attribute on Properties would not be resolved until the constructor finishes. That is why you would need to inject the EventAggregator dependency through the constructor as a parameter if this dependency would be used on the constructor implementation.
Therefore, if you would like to use the EventAggregator dependency on the ViewModel constructor, you should use [ImportConstructor] attribute instead, and ask the container for the EventAggregator instance by retreiving it as a parameter:
public class MainViewModel
{
private readonly IEventAggregator eventAggregator;
[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.eventAggregator.GetEvent<MyEvent>().Publish("");
}
}
You may find more related information regarding both import alternatives in the following post:
MEF - [Import] vs [ImportingConstructor]
I hope this helped you, Regards.
In your bootstrapper class have this method :
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.ComposeExportedValue(new EventAggregator());
}
You should look at this article as it is answer both your first and second question in better details.
http://www.gonetdotnet.info/posts/wpf-articles/wpf-module-communication
Update:
If you create a class as below it will match your export with your import.
public class EventAggProvider
{
[Export(typeof(IEventAggregator))]
public IEventAggregator eventAggregator { get { return new EventAggregator(); } }
}
It is a little late, but there is also a solution for the case in which you want the EventAggregator gets injected in property. Implement 'IPartImportsSatisfiedNotification' and use the eventaggregator in 'OnImportsSatisfied' method.
public class MainViewModel : BindableBase, IPartImportsSatisfiedNotification
{
[Import]
public IEventAggregator eventAggregator;
public void OnImportsSatisfied()
{
eventAggregator.GetEvent<AppExitEvent>().Publish("");
}
}
EventAggregator does not depend on MEF or Unity it's design pattern coined by Martin Fowler and it's based on publisher subscriber scenario
try to follow this steps
//this should be in your infrastructure layer
public static class EventAggregatorHelper
{
private static IEventAggregator _Current = new EventAggregator();
public static IEventAggregator Current
{
get
{
return _Current;
}
}
}
The HandleAppExitEvent is a class declared as shown below:
public class AppExitEvent : CompositePresentationEvent<String>
{
}
and the subscriber would be something like this:
in your MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
//In this case the HandleAppExitEvent and the subscriber that deals with this event.
EventAggregatorHelper.Current.GetEvent<AppExitEvent>(). Subscribe(HandleAppExitEvent);
}
//note that this method should be public
public void HandleAppExitEvent(string mess)
{
if (!String.IsNullOrEmpty(mess))
{
//Do whatever you need to do here.
}
}
}

how to add many regions into mef container

i am trying to register multiple regions to my region like in Unity:
public class ModuleA : IModule
{
private IUnityContainer _container;
private IRegionManager _regionManager;
public ModuleA(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
public void Initialize()
{
IRegion region = _regionManager.Regions[RegionNames.ToolbarRegion];
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
region.Add(_container.Resolve<ToolbarView>());
}
}
}
but i can't succeed to do it with MEF
[ModuleExport(typeof(ModuleAModule))]
public class ModuleAModule : IModule
{
[Import]
private IRegionManager _regionManager;
public void Initialize()
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ContentView));
IRegion region = _regionManager.Regions[RegionNames.ToolbarRegion];
}
}
i can get the region, but i don't know how to translate the resolving into MEF.

PRISM View Injection/Navigation in Same Module

This is ModuleInit.cs in Products module
public class ModuleInit : IModule
{
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
public ModuleInit(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
#region IModule Members
public void Initialize()
{
App app = (App)Application.Current;
_regionManager.RegisterViewWithRegion(RegionNames.ModuleRegionProducts, () => _container.Resolve<Views.ProductsCycle>());
}
#endregion
}
Below is button event handler in ProductsCycle.cs to go to another view still within same module:
private void btnForward_Click(object sender, RoutedEventArgs e)
{
IRegion productsRegion = _regionManager.Regions["ModuleRegionProducts"];
var productsListView = _container.Resolve<Views.ProductsList>();
productsRegion.Add(productsListView, "ProductsList");
productsRegion.Activate(productsListView);
}
State: ProductsCycle page is successfully loaded on first load.
Problem: View doesn't changed from ProductCycle page to ProductsList page when btnForward is clicked.
I'm using Silverlight 4 and PRISM2.
Please your advice, thank you.
change productsRegion.Activate(productsListView);
to productsRegion.Remove(this);
because my productListView is not inherited directly from UserControl.

Categories

Resources