I'm very new to modules in Prism and am struggling to understand how to use them properly. I want to have a navigation panel like in the image below, which I stole from Google.
Let's say that I have 3 modules for 3 separate concerns: NavigationModule, HelpModule, and FeedbackModule. If I want to show "Help" when the app first loads, my understanding is that I would do something like the following:
Define Regions for navigation and content in my Shell (main WPF project)
Upon execution, load the navigation view into the navigation Region
Upon initialization of the NavigationModule, load a view from HelpModule into the main content Region
This leaves me with a few questions about what modules should know about each other:
Should the Shell's project have a reference to the NavigationModule in order to load its view?
Should the NavigationModule have a reference to the HelpModule in order to load its view? And a reference to FeedbackModule in order to load its views on demand?
If the answer to these questions is "no," then what's the best way for modules to be aware of each other's views? I could create a shared class library with constants for view names, but it seems a bit troublesome to maintain a bunch of strings that way whereas with references I could use nameof(). I would appreciate any direction. Thanks.
Modules should not "know" each other in the sense of having a project reference from one module to another, because that kind of defeats anything gained from having modules in the first place, that is, to have a modular application (built of components that can be swapped out independent of each other).
Modules should interact through shared interfaces, which are define outside of modules, that is either in the framework or in assemblies that "are" no modules themselves. Depending on your requirements, you should define upfront (and enforce in the build process) which modules there are and which interface-assemblies and which module is allowed to reference which interface-assembly.
So how to show the Help-view when the application loads? Send a message (e.g. via IEventAggregator or any other communication mechanism) when it's time to load the initial view (whatever that may be). The Help-module listens for the message and navigates to the Help-view. Important: the SessionResume-module might also listen for our message. It's the responsibility of whomever choses which modules to deploy to only deploy modules that are compatible with each other.
Related
I'm architecting a project where we have one module acting as our shell, handling global styles, navigation, etc, with an ng:view getting filled by directives provided by our second module with all the pages.
I would really like to find a way for the shell to inspect the pages module, discovering the nav structure dynamically, rather than defining it by hand or having every page inject itself both in the pages module AND to the shell module. On its own, the injector doesn't seem to like doing that.
EDIT
As clarification: while I have a fabulous workaround with a pagesProvider service, I am still looking for an answer as to whether Angular 1.2.x has a mechanism to introspect modules.
Firstly: at the end all injected objects (like services, filters, controllers) will end up in one big bag (something like global namespace). So it is enough to inject each page into pages and pages into shell. (mind the possibility of name-conflicts.)
Secondly: at this time, there is no support for lazy loading of modules. So you should know all your modules beforehand, so there is actually no need to check which objects are present and which not, as either there are all of them or the application has not started at all.
I do not know what you actually mean by discovering nav structure and how it will be defined. But, what you can do is:
in each page-module define a constant in which the nav structure of this page will be stored.
access this constant from shell when needed. If you need to know dynamically the idof active page, just set this information somewhere -- probably as value -- when the pages gets activated.
Some alternatives:
define one global hash-map from page-ids to page-nav-structures.
use a common service that will be injected to each page and will be used to register pages and all related information about it.
I need to create plugins that hook into the functionality of my main application. CakePHP plugins only instantiate themselves when its own controller is called meaning I cannot affect the processes of my main application.
mainapp/action2baffected
myplugin/
I like the idea of having self contained pluggable models , is there any other way to get this to work? Creating models on the fly etc or write a plugin system from scratch with no cakeiness!
You can use components and behaviors (from the plugins) into your core application. There is one very good presentation of Pierre MARTIN Using reusing-plugins. It's a really inspirational resource.
We have put quite a lot of work into making plugins truly self contained in Infinitas
You can have a look at some of the methods that are used, but the main code is in the events. Everything from cache configs, db connections and include assets like css/js are done from within the plugin, even injecting some markup into views is handled.
I have an application designed using Microsoft's Composite Application Library. My shell has several regions defined so that I can inject content from separate modules. I'm looking for a design pattern that will reduce the coupling that these regions introduce.
In all examples I have seen, regions are defined and accessed using a string in a static class in the infrastructure project.:
<ItemsControl cal:RegionManager.RegionName="{x:Static inf:RegionNames.TabRegion}">
public static class RegionNames
{
public const string TabRegion = "TabRegion";
}
This introduces an dependency on the shell from the infrastructure project, because part of the infrastructure project must now match the shell. The CAL RegionManager throws an exception if you attempt to access a region which is not defined, so I must ensure that the infrastructure and shell projects are kept in sync.
Is there a way to isolate the shell's regions so that they are defined only within the shell (no region names in the infrastructure project)?
Is there a way to make regions optional, so that shells can be swapped out even if they don't have all the same regions? (An example: One shell has menu and toolbar regions, another only has the menu... modules should be able to inject into the toolbar if it's available, without failing when it's not)
Update - More details on my architecture
In response to depictureboy's answer below, I wanted to describe the way my system is set up... perhaps there will be more good feedback on it.
I am treating the Infrastructure and Shell projects as generic libraries, and I have several applications which use them. The Infrastructure project provides "framework" code and resources (like MVVM stuff, reflection, icons), and my Shell is a generic host window, with the basic window layout (menus, toolbars, status bar, main content area). The applications all share a common look and behave similarly because they share the shell.
My applications get their individual functionality from the modules which get loaded, so I have a bootstrapper project per application which pulls everything together (infra, shell, modules).
I imagine if I ever need to develop a brand new application that is very different from the current ones, I will be able to re-use the infrastructure project, but not the shell. That is why I am curious about decoupling the infrastructure project and the shell.
I think you have your logic backwards. Your shell is the glue that binds everything together. In my mind you want the infrastructure and shell tightly coupled because they are the application. Your modules are the parts of the application that will be changing and switching around dynamically. You want your shell regions to be static so that, for example, another developer could write a module for your application knowing where his different views were going to be placed and how the application should behave with his module attached. The Infrastructure project is there to be the go between between your shell and its modules...thats just a fact of life at least in my book. One of the WPF gurus may come up with something that absolutely blows that out of the water tomorrow....
In a Composite WPF application, what is the best way to store global variables needed by several modules? For example, I am working on an application in which several modules need to get a file name, so they can fetch the data they need from the file.
Is there a best practice for storing information like this in a Composite WPF app? How do I get the information to my modules while still keeping loose couplings? Thanks for your help
David Veeneman
Foresight Systems
Write a service that encapsulates the logic you require and package that service into a module. Then have your other modules use that service to get their job done. Note that the service may expose the file name directly, or may instead choose to expose operations that operate on an underlying file without consumers being aware of said file.
Create an interface who's responsibility it is to return the "selected file name". Unlike most services / dependencies, it won't do a lot of processing - it's just responsible for returning a value. Use dependency injection to provide an implementor of this service to all places that need it.
At the moment this file name might seem truly global, but imagine your app had to transition from SDI to MDI. It's never a good idea to have true singletons in you composite apps.
Thanks for both answers, both of which look very good. I came up with a third approach while out on my morning run, and I think I'm going to try this one:
I load all of the modules in my Composite WPF app at startup and activate only the views that will be shown initially. So, all of my modules, even the ones not shown, are available as soon as startup completes.
When they are initialized, each module that needs the file path will subscribe to a FileOpened composite event in the Prism event aggregator. When a file is opened from the Shell, the Shell View Model will publish a FileOpened composite event. The composite event will carry the file path as its payload.
So, when the FileOpened event is published by the Shell view model, the appropriate callback method in each module will be called by the Prism event aggregator, and the filePath will be passed to each module's view model.
In my dotnetnuke application i added so many modules, i want to display the content in the contentpane when i clicked on the items that is in in the left / right pane. Can you give me the code for navigating the modules..
There are a few options available to you for passing information from one module to another. The simple way is to put the shared state on the querystring.
The more robust way is to use Inter-Module Communication, which basically requires one module to implement the IModuleCommunicator interface, allowing it to send massages. You can then implement the IModuleListener interface in other modules to react to those messages.
It's hard to answer this question because I don't know what you're doing exactly. What is the module in the left pane, and what is the module in the content pane? How are they interacting?