Possible to get a list of all registered modules?

Topics: CAB & Smart Client Software Factory
Jan 16, 2006 at 5:37 PM
originally posted by: WateryDan

I'd like to know if it is possible to get a list of all registered modules in CAB? I need them so I can show them as navigation items on the dynamically built menus.

Thanks...
Jan 17, 2006 at 5:46 AM
originally posted by: BradWilsonMSFT

Yes. Retrieve the IModuleLoaderService service. It has a property called LoadedModules, which returns a collection of LoadedModuleInfo.
Jan 17, 2006 at 1:49 PM
originally posted by: WateryDan

Thanks for the reply... It works great!! Is it also possible to get all work items for a particular module?
Jan 18, 2006 at 9:14 AM
originally posted by: aleung61

I'm trying to do something similar, but it doesn't appear that LoadedModuleInfo gives you anything useful to navigate with -- like the module workitems. (WateryDan -- I'm guessing that was the reason for your follow up question. If you figure this out, please post)

I saw another post that suggested creating your own service that each module would register with when loaded. Is that the way to go?
Jan 18, 2006 at 10:20 AM
originally posted by: dkozak

I am also struggling with the same issues that WateryDan is experiencing. Is there a C# and VB example that would provide a guide on how to develop a CAB application with the following criteria.

1. A Shell with both a Navigation Workspace and Main menu Toolstrip
2. Two modules (seperate DLL's) that are loaded into the shell at startup
3. Raising of the Loaded Events for the modules up to the Shell
4. The loaded event then constructs the Navigation Bar creating Bars displaying the friendly name of the the module name and wiring up events so that clicking on the NAvigation bar wil activate the specified view
5. The loaded event then constructs the Main menu Toolstrip based on the module loaded

I guess where I am struggling most is trying to establish the best way to avoid tightly coupling the module to the shell but still display menu and navigation items that are module specific. Can you provide a little guidance in this area?

Regards,

David
Jan 18, 2006 at 1:16 PM
originally posted by: WateryDan

Your critiria sounds similar to what I'm doing. I'm by no means experts in CAB, but this is what I came up with:

1. I have several modules (DLLs), each with their own ModuleInit derived class.
2. Within each module, I have a XML (Navigation.xml) file that lists all the navigation items to go to the MainMenu and a TreeView in ShellForm.
3. Each WorkItem within a module inherits from an interface which has 1 method: ShowDefaultView.
3. When the shell loads, I use the ProfileCatalog.xml to determine what modules are included. Then I use reflection to dig into those DLLs to find the Navigation.xml and construct my nav menu and treeview.
4. When a user clicks on the nav menu or treeview, I then load up the WorkItem and use ShowDefaultView to show the UserControl inside a workspace.

It works fine so far though I'm not sure whether this is the best approach. Hope it helps...
Jan 18, 2006 at 2:20 PM
originally posted by: dkozak

Thank you.

Everything you are doing makes sense to me except Number 3 (actually the second number 3).
It is probably because I am still new to CAB and not terribly strong on reflection, but how do you use the relection on the module when one of the goals is to keep the modules loosely coupled from the shell? I am guessing that you retrieve the module name as per Brad's comments above and then use the name to execute reflection on the module. But even so, do you not require at least a mutual Interface to be referenced in both the Shell and the Modules or do you essentially use a hardcoded property name (in the shell) that you are looking for in the reflected module? Hope that makes sense.... Also, does each Module DLL have a different name for the Navigation.xml file or are they in seperate directories?

The approach that I would like to use is different however. I would like to setup Subscriber/Publisher events in both the shell and the modules. Such that if you click on a Navigation workspace item it fires a "Navigation" Event that is subscribed to by all the Modules. The (appropriate) module would then display in the Module workspace and raise a published "ModuleLoaded" event back to the shell (passing the Navigation information as an EventArg. The shell would then receive the subscribed event and Change the Menus and Toolstrips appropriately. The trick i think is how to setup the appropriate Events to make this work efficiently

In addition to a ModuleLoaded event I also see a need to be able to change the menus on the fly as well. For example you may need to disable the Save menu until such time as the open document has changes... I am not sure how you would address that using your approach. Presumeably Events could address this but it may be ugly trying to figure out which dynamic menu item is being modified...

One approach that I have not totally ruled out yet (but havent implemented either) is to actually pass a reference to the Menus, Toolbars, and Navigation panel objects to the Module as EventArgs in the Navigation Event and the module can change the menus, etc. directly. I think this approach would be fairly clean but I am concerned about the increased coupling that might result from this approach (I need to think about it more)

Do you see any obvious holes in either of these approaches or am i missing sme important points in your approach? Any criticism, good or bad would be appreciated.

Cheers,

David
Jan 19, 2006 at 8:42 AM
originally posted by: dkozak

A few hours of reading the docs and scanning of the message boards later.... :-)

Thanks to another posting I was able to look at the new SCBAT sample:

http://www.gotdotnet.com/codegallery/codegallery.aspx?id=941d2228-3bb5-42fd-8004-c08595821170

The menu item adjustments that I want to make in my application would appear to be best addressed (as presented in the SCBAT sample) via "WorkItem.UIExtensionSites" for the Status, Toolbar, and Main menu. Presumeably the Navigation Panel can be similarly managed. At this point my research on this issue is still theoretical. Now I just need to code a sample.

Cheers,

David
Jan 19, 2006 at 1:52 PM
originally posted by: WateryDan

Hi David,

I've tried most methods that you described in your previous posts. Most of these work fine if it is a simple solution, but when you want to develop a plug-in style application where you want to drop a DLL into a bin directory and it just works, these methods fall apart.

Using UIExtensionSites is a workable solution, but it lacks one major thing: it can't get the control instance from its wrapper (please refer to my previous post about this question). Therefore, you can't determine the order each menu item will be inserted. For my solution, I also want all the navigations to be controlled centrally as the user has more than a few options to navigate around the system (navbar, menu, treeview, etc). To use UIExtensionSite to achieve this is feasible but messy. That's why I have an XML file and a central place to control all the navigation items.

One other major issue with menus constructed dynamically is their events. EventBroker uses EventTopics to determine the publisher and subscriber. Since EventTopic is a declarative attribute, it can't be changed at runtime. The only way to get around it is to publish an event with a EventArg that contains its origin. Then every module has an EventSubscription that looks into the EventArg to figure out if this event actually belong to this module. This is an issue caused by drawing nav items dynamically. If you are using UIExtensionSites, this will not be a problem because you'll already have the event wired before adding items to the UIExtensionSites.

Hope this gives you some background of my approach. Any feedback is more than welcome!
Jan 19, 2006 at 2:19 PM
originally posted by: dkozak

Just a clarification on the Navigation XML file.

Is it a single file per module (and how do you manage it? seperate names/directories?) or is a single file for all modules that you make insertions/modifications into as you add modules?

Thanks,

David
Jan 19, 2006 at 2:29 PM
originally posted by: WateryDan

Each module has its own XML file as a resource. When a module is loaded, I use reflection to look for it.
Jan 19, 2006 at 3:53 PM
originally posted by: aleung61

Just to follow up... I tried creating a service to keep track of the modules and it worked pretty well. Granted, this was just a simple prototype for fun, but it seemed to have some merit.

I created a simple service that has...

methods...
public void AddNew(string module)
public void Activate(string module)
properties...
List<string> modules
events...
ActivateModule(string module)

The first module loaded starts the service and adds it to the rootWorkItem.
In each module's moduleinit class, I call AddNew of the service to register an identifier for the module.
The main workitem in each module subscribes to the ActivateModule event.
Any module that uses the service can present a list of module identifier.
Any module that uses the service can call the Activate on the service and provide an identifier.
When the mainworkitem of the module gets the event, it looks to see if the string passed in matches its identifer. If it does it displays its view in the workspace.

For the menu items, I was going the way David went with the UIExtensionSites. It looks like you can get pretty good control of position if you register the proper entry point (can't remeber what the proper term is) and you can keep a copy of the menustripitem in your workitem to manipulate the menu item.

...just another possibility.
Jan 21, 2006 at 8:52 AM
originally posted by: dkozak

This sounds like an excellent approach for handling the modules because (for the Navigation Panel) I am really only interested in the "Friendly" name for the module (ie. "Contacts", "Orders") not necessarily the name of the module. The activation approach also sounds promising... I'll have to give it a try.

Thanks for your input.

David
Feb 7, 2006 at 12:49 PM
originally posted by: AndrePiwoni

If you need WorkItems in module once it is loaded,
you could do the following in ModuleInit

public override void Load()
{
base.Load();
IWorkItemTypeCatalogService.RegisterWorkItem<YourWorkItem>();
}

Then when you need it:

ICollection<Type> workItemTypes = catalogService.RegisteredWorkItemTypes;

foreach (Type workItemType in workItemTypes)
{
//do something
}