Recently I’ve been implementing right-click context menus in our application. The problems I encountered and the solution I reached may not be applicable to all CAB applications so I’ll give you the context (apologies for the bad joke!)
The application is a composite style application using the Microsoft Composite Application Block (CAB) libraries. There are several independent couples that can be included/excluded without affecting the other modules. There is a common Domain Model that represents the pure business domain of the application; each module largely just acts as a UI/View for an aspect of the model. The Shell of the application makes use of the Infragistics ToolbarManager (Office 2007 ribbon style).
The requirements I was trying to implement stated that if a representation of an entity in the domain model was right clicked on in any of the modules a context menu should be shown showing several commands that can be chosen for that entity. The commands in the context menu may act on one or more modules.
Problem 1 – Where should the code live?
The first problem I encountered was where should the context menus be created? If created in one/all of the modules then how do we maintain inter-module independence? We could end up with the same context menu duplicated in several of the modules. We could end up with a context menu that contains commands that act on a module that hasn’t been included. We could end up with modules communicating with each other (through some sort of interface to register commands with each other’s context menus – like UIExtensionSites in CAB).
So if not in a module then perhaps in the shell? This has the problem that the shell is generic and not tied to the domain (we even use the same shell in another application); it would be undesirable for the shell to be dependent upon domain model concepts.
This leaves the domain model as a possibiltiy, however, as we don’t want UI concerns in the domain model (like context menus). We need to create a part of the domain model that is about UI, it will act pretty much like it’s own module but will be a dependency for all/many of the modules (I believe the Smart Client Software Factory calls these Foundation Modules).
Problem 2 – What are the responsibility boundaries and the interfaces?
I decided that as much of the definition of the context menu should be in a ContextMenuService in the new domain.UI project. This class would define all the tools on the menu and interact with the shell to add them (The ToolbarManager needs the tools to be registered as root tools in the shell). Any tool that acted on a module (or many modules), would start off invisible; When the module is loaded it will communicate with the ContextMenuService and specify the tool keys that it will respond to, the first module to specify a tool will make the tool visible in the context menu (subsequent modules will have no effect).
Each module will handle the right-click event and tell the ContextMenuService to show the context menu for the clicked on instance of the domain entity. (This will also ‘set the context’ for the menu modifying menu options based on the instance). Finally the ContextMenuService will call through to the Shell to instruct it to show the context menu at the current mouse location.