Click here to Skip to main content
Email Password   helpLost your password?

Introduction

A Microsoft patterns & practices Composite UI Application Block (CAB) based module composite mapper service is provided including C# source code that builds Workspaces, UIElements, Commands and Event Publications/Subscriptions using an XML configuration file specified with a module. The module composite mapper service is based and executes on the CAB using a Model-View-Presenter (MVP) pattern within a proof-of-concept (POC) framework. This article and its included source code provide POC framework classes, module composite mapper service classes, module composite mapper XML configuration file syntax and examples, module composite mapper topic format guidelines, and example/template modules.

In addition to POC framework and module composite mapper service items described in further detail, the following CAB items are referenced throughout the context of this article and are defined in the CAB documentation similar to the following:

Proof-of-Concept Framework

The module composite mapper service is based and executes on the CAB using a Model-View-Presenter (MVP) pattern within a proof-of-concept (POC) framework. While the POC framework can be modified (or removed altogether) based on your needs, the following describes the POC framework base classes in further detail. Note that the POC framework shell uses the native CAB reflection-based module loader service against a local startup directory.

Module Composite Mapper Service

The module composite mapper service allows a module developer to specify the following composite types using a module composite mapper XML configuration file (syntax is described in further detail). Note that composite is used as a generic term to describe any one of the following items:

A module developer minimally interacts with the module composite mapper service through the following items only (contained in the Services.XSModuleCompMppr namespace for any module):

Module Composite Mapper Service Classes

Module Composite Mapper XML Configuration File Syntax and Examples (XSModuleCompMppr.xml)

The syntax of the module composite mapper XML configuration file is provided in detail below including example composite specifications. The module composite mapper service currently requires the following of the configuration file in order to function properly:

XSModuleCompMppr.xml Syntax

<XtensibleSolutions version="2.0" language="XSModuleCompMppr">
    <!--
Module composite mapper contains composite mappers: 
         * Workspace (optional; applies to shell presenter only)
         * UI extension site (optional)
         * Command (optional)
         * Event (optional) 
            NOTE: ALL WorkItems/Presenters publish generic events.
-->
    <
Module_Composite_Mapper>
      <!--
Workspace composite mapper contains workspace composites:
            * Workspace type descriptor (optional; for readability only) and... 
            * Workspace type enumeration value (required)
            * Workspace topic (required)
-->
      <
Workspace_Composite_Mapper>
          <
Workspace_Composite
             
XSWorkspaceType_Descriptor="Foo"
              XSWorkspaceType_Enum="0"
              Workspace_Topic="wks://XS/Shell/Foo"/>
      <!--
UI extension site composite mapper contains UI extension site composites: 
           * Parent topic (required)
          
* Insert priority (optional - defaults to 0.0; used to indicate an 
              insertion priority for positioning the UI element in relation to its sibling 
              items); if not populated, the null priority is assigned (0.0) and the default 
              CAB behavior is used for the requested insertion (appended as the last item); 
              if populated (with a float value), the indicated priority is assigned and 
              used for positioning the requested insertion where: 
              - the smaller the indicated magnitude, the higher its priority and the closer 
                the item will be to the top of the list if not the first (i.e., 1.0 is 
                considered "priority one", though the actual algorithm has no practical limit 
                on the lower bounds while also considering the special value of 0.0)
              - the higher the indicated magnitude, the lower its priority and the closer 
                the item will be to the bottom of the list if not the last (e.g., 99.0 is 
                lower priority than 1.0)
              - equivalent priorities result in the existing item taking precedence (i.e., the
                new item is inserted immediately after that which it is equal to in priority)
              - an assigned priority always takes precedence over those not assigned 
                that are appended as the last items (e.g., 99.0 is higher priority 
                than 0.0)

           * UI element Type (optional - defaults to "ToolStripMenuItem")
           * UI element properties (optional; a generic attribute with 
              property|value|property|value|... pairs used to configure 
              virtually any property on the created UI element); currently 
              supported property types include strings, booleans, and images...

              Examples: 

              "Text|&amp;File": sets the Text property on the UI element to "File" 
              with the "F" specified as the UI element mnemonic

              "Checked|true": sets the Checked property on the UI element to True

              "Image|MyImageFile.ico": sets the Image property on the UI element 
              from the image file as specified (e.g., toolbar icons); images must 
              be named per the property specification (e.g., MyImageFile.ico), 
              co-located alongside the composite mapper file in the XSModuleCompMppr 
              folder, and compiled as an embedded resource

           * Register topic (optional) and...
           * Register property (optional - defaults to "DropDownItems"; used to 
              indicate which property on the created UI element is registered as 
              an extension point)
           * Command topic (optional) and...
           * Command event name (optional - defaults to "Click")
-->
      <
UIExtensionSite_Composite_Mapper>
          <
UIExtensionSite_Composite 
             
Parent_Topic="ste://XS/Shell/Menu"
              Insert_Priority="1.0"

              UIElement_Type="ToolStripMenuItem" 
              UIElement_Properties="Text|&amp;File"
              Register_Topic="ste://XS/Shell/Menu/File"
              Register_Property="DropDownItems"
              Command_Topic="cmd://XS/Shell/File_Click"
              Command_EventName="Click"/>
      </
UIExtensionSite_Composite_Mapper>
      <!--
Command composite mapper contains command composites: 
           * Command topic (required)
           * Command handler method name (required)
-->
      <
Command_Composite_Mapper>
          <
Command_Composite 
             
Command_Topic="cmd://XS/Shell/FileExit_Click"
              Command_HandlerName="OnFileExit_Click"/>
      </
Command_Composite_Mapper>
      <!--
NOTE: ALL WorkItems/Presenters publish generic events.-->
      <!--
Event publication composite mapper contains event publication composites: 
           * Event topic (required)
           * Publication scope descriptor (optional; for readability only) and... 
           * Publication scope enumeration value (optional - defaults to 
             Global {0}); values include (see the CAB help for more info): 
                 - Global {0}
                 - WorkItem {1}
                 - Descendants {2} 
         * Publisher type descriptor (optional; for readability only) and... 
         * Publisher type enumeration value (optional - defaults to WorkItem {0}); 
            values include: 
                - WorkItem {0}
                - Presenter {1} 
         * Publication handler method name (required)
-->
      <
EventPublication_Composite_Mapper>
          <
EventPublication_Composite 
             
Event_Topic="evt://XS/MyModule/MyEvent"
              PublicationScope_Descriptor="Global"
              PublicationScope_Enum="0"
              XSEventPubSubType_Descriptor="WorkItem"
              XSEventPubSubType_Enum="0" 
              Publication_HandlerName="BroadcastMyEvent"/>
      </
EventPublication_Composite_Mapper>
      <!--
Event subscription composite mapper contains event subscription composites: 
           * Event topic (required)
           * Subscriber type descriptor (optional; for readability only) and... 
           * Subscriber type enumeration value (optional - defaults to WorkItem {0}); 
              values include: 
                  - WorkItem {0}
                  - Presenter {1} 
           * Subscription method name (required)
-->
      <
EventSubscription_Composite_Mapper>
          <
EventSubscription_Composite 
             
Event_Topic="evt://XS/MyModule/MyEvent"
              XSEventPubSubType_Descriptor="WorkItem"
              XSEventPubSubType_Enum="0" 
              Subscription_MethodName="OnReceiveMyEvent"/>
      </
EventSubscription_Composite_Mapper>
    </
Module_Composite_Mapper>
</
XtensibleSolutions>

Module Composite Mapper Topic Formats

The following sub-sections provide guidance on standard module composite topic formats including examples. The examples use parameter strings and method attributes for context and clarity whereas the module composite mapper service actually alleviates the need for these decorations through use of the single-point of configuration module composite mapper file.

Workspace Topic Format

wks (workspace)
XS (Xtensible Solutions, Inc. or end-user company)
ModuleName (or Generic if application-wide)
Descriptor00/Descriptor01/...

(client-defined; workspace topic descriptor should use the relevant XSWorkspaceType name followed by a brief indication of the specific instance, such as WorkspaceTypeFooMain)

Examples

this.RootWorkItem.Workspaces.Add(
   
this.Shell.GetWorkspace(XSWorkspaceType.WorkspaceTypeFoo),
    "wks://XS/Shell/WorkspaceTypeFooMain"
);

this.RootWorkItem.Workspaces["wks://XS/Shell/WorkspaceTypeFooMain"].Show(this.view);

UIExtensionSite Topic Format

ste (UIExtensionSite)
XS (Xtensible Solutions, Inc. or end-user company)
ModuleName (or Generic if application-wide)
Descriptor00/Descriptor01/...

(client-defined; site topic descriptors should use the path to the UIElement of interest)

Examples

this.RootWorkItem.UIExtensionSites.RegisterSite(
   
"ste://XS/Shell/Menu", this.Shell.MainMenuStrip);

this.RootWorkItem.UIExtensionSites.RegisterSite(
    "ste://XS/Shell/Menu/File", fileMenuItem);

Command Topic Format

cmd (command)
XS (Xtensible Solutions, Inc. or end-user company)
ModuleName (or Generic if application-wide)
Descriptor_Event

(client-defined; command topic descriptor should use a brief indication of the event followed by the actual event being handled, such as "FileExit_Click")

Command Invoker: Descriptor_Event (e.g., FileExit_Click)
Command Handler Method: OnDescriptor_Event (e.g., OnFileExit_Click)

Examples

Command Invoker

workItem.Commands["cmd://XS/Shell/FileExit_Click"].AddInvoker(fileExitMenuItem, "Click");

Command Handler

[CommandHandler("cmd://XS/Shell/FileExit_Click")]
public void OnFileExit_Click(object sender, EventArgs e) { myObject.MyMethod(); }

Event Topic Format

evt (event)
XS (Xtensible Solutions, Inc. or end-user company)
ModuleName (or Generic if application-wide)
Descriptor

(client-defined; event topic descriptor should use a brief indication of the event to be broadcast)

Event Publication Handler: BroadcastDescriptor (e.g., BroadcastFoo)
Event Subscription Method: OnReceiveDescriptor (e.g., OnReceiveFoo)

Examples

Event Publication

The publication handler itself...

[EventPublication("evt://XS/Generic/Foo")]
public event EventHandler<DataEventArgs<string>> BroadcastFoo;

Public method for invoking the publication handler from referring classes. While not required for events that should not be invoked except by the publishing class itself, if appropriate, provides an interface for referring classes that may also require invocation of the event, such as a view that may invoke an event defined on its owning presenter...

public void DoBroadcastFoo(object sender, string thisMessage)
{
   
this.BroadcastFoo(sender, new DataEventArgs<string>(thisMessage));
}

Event Subscription

[EventSubscription("evt://XS/Generic/Foo")]
[
EventSubscription("evt://XS/Generic/Bar")]
public void OnReceiveGeneric(object sender, DataEventArgs<string> e)
{
    myObject.MyMethod(e.Data);
}

Example Modules

XSShell

The XSShell application module provides core UI items such as a menu, toolbar and workspaces.

Sample screenshot

In the POC framework, the XSShellApplication class overrides the AfterShellCreated method to register core UIExtensionSites in support of the module composite mapper service:

this.RootWorkItem.UIExtensionSites.RegisterSite(
 
"ste://XS/Shell/Menu",
 
XSToolStripUIAdapterFactory.GetAdapter(this.Shell.MainMenuStrip));
this.RootWorkItem.UIExtensionSites.RegisterSite(
 
"ste://XS/Shell/Toolbar",
 
XSToolStripUIAdapterFactory.GetAdapter(this.Shell.MainToolStrip));

MyServerModule

The MyServerModule provides a user interface on the shell that dynamically loads another module (MyClientModule) during application run-time.

Sample screenshot

  • Module Composite Mapper File (XSModuleCompMppr.xml)

    Specifies a user interface element on the standard tool menu

    <UIExtensionSite_Composite
       
    Parent_Topic="ste://XS/Shell/Menu/Tools"
        Insert_Priority="1.0"
       
    UIElement_Properties="Text|&amp;Load Client"
       
    Command_Topic="cmd://XS/MyServerModule/ToolsLoadClientModule_Click"/>

    Specifies a command handler for the UI element on the presenter

    <Command_Composite
       
    Command_Topic="cmd://XS/MyServerModule/ToolsLoadClientModule_Click"
       
    Command_HandlerName="OnToolsLoadClientModule_Click"/>

    Specifies an event subscription to the MyClientModule ClientEvent

    <EventSubscription_Composite
       
    Event_Topic="evt://XS/MyClientModule/ClientEvent"
       
    Subscription_MethodName="OnReceiveClientEvent"/>

  • Presenter Class (MyServerPresenter): defines a command handler (OnToolsLoadClientModule_Click) that...

    Disables the user interface element on the standard tool menu
    Calls LoadClientModule on the work item

    Sample screenshot

  • View Class (XSView): no module-specific view class is defined (the base view implementation is instantiated on the presenter)
  • Work Item Class (MyServerWorkItem):

    Defines a method (LoadClientModule) that loads another module using the module loader service (MyClientModule with a hard-coded path)
    Defines a subscription method (OnReceiveClientEvent) that displays a message box on receiving the MyClientModule ClientEvent

    Sample screenshot

MyClientModule

The MyClientModule provides a user interface on the shell that allows the end-user to broadcast a module-defined event across the application, as well as show or hide its view.

Sample screenshot

  • Module Composite Mapper File (XSModuleCompMppr.xml)

    Specifies user interface elements on the standard view menu and toolbar (note the Checked and Image property settings)

    <UIExtensionSite_Composite
       
    Parent_Topic="ste://XS/Shell/Menu/View"
        Insert_Priority="1.0"
       
    UIElement_Properties="Text|&amp;Client|Checked|true"
       
    Command_Topic="cmd://XS/MyClientModule/ViewMyClient_Click"/>

    <
    UIExtensionSite_Composite
       
    Parent_Topic="ste://XS/Shell/Toolbar"
        Insert_Priority="1.0"
       
    UIElement_Type="ToolStripButton"
       
    UIElement_Properties="Image|LoadClientModule.ico|Checked|true"
       
    Command_Topic="cmd://XS/MyClientModule/ViewMyClient_Click"/>

    Specifies a command handler for the UI elements on the presenter

    <Command_Composite
       
    Command_Topic="cmd://XS/MyClientModule/ViewMyClient_Click"
       
    Command_HandlerName="OnViewMyClient_Click"/>

    Publishes a module-defined event with its publication handler on the presenter


    <
    EventPublication_Composite
       
    Event_Topic="evt://XS/MyClientModule/ClientEvent"
       
    XSEventPubSubType_Enum="1"
       
    Publication_HandlerName="BroadcastClientEvent"/>

  • Presenter Class (MyClientPresenter):

    On construction displays the view on the Bar (versus Foo) workspace
    Defines a publication handler (BroadcastClientEvent) that broadcasts the module-defined event
    Defines a command handler (OnViewMyClient_Click) that shows/hides its view

    Sample screenshot

    The OnViewMyClient_Click command handler sets the view visibility and uses the presenter
    UIExtSiteCompMppr.GetUIElements method to set the menu item and toolbar item Checked property on command invocation:

    bool visible = (!(view.Visible));
    foreach (ToolStripItem uiElement in
           
    this.UIExtSiteCompMppr.GetUIElements(((Command)sender).Name))
        uiElement.GetType().GetProperty(
    "Checked").SetValue(uiElement, visible, null);
    view.Visible = visible;

  • View Class (MyClientView): defines a local user interface event (uiButtonBroadcastClientEvent_Click) that...

    Invokes the publication handler on the presenter (DoBroadcastClientEvent)

    Sample screenshot

Conclusion

The module composite mapper service provides a consistent, XML configurable, and single per-module persistence paradigm for dynamically building workspaces, UIElements, commands, and event publications/subscriptions within a CAB based application. Regardless of any up and coming composite builder architecture implemented by the Microsoft patterns & practices CAB team, the module composite mapper service provides a clear, concise and well-segregated framework for moving from the current (relatively undefined) approach into any production-oriented module composite development process. In fact, given the XML configuration oriented composite builder snippets available to date, albeit generally limited in scope compared to the module composite mapper service, one should be able to migrate relatively seamless from (or integrate across) the module composite mapper service and any successor approach.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCulture problem...
ikharus
12:03 12 Dec '06  
First of all, sorry for my english, I'm french, but i'll do my best to be clear enough.

This project is very very nice and usefull. But at the beginning, i was not able to run the solution, I had many "Input string was not in a correct format" (FormatException).

Finally, I realize that my default culture was "fr-CA" (french-Canada). It gives me some headache, like how to manage the "Insert_Priority" in xml mapper file (XSModuleCompMppr.xml). With the culture fr-CA, it should be 5,0 instead of 5.0 for example. I finally tried the following in the Shell, to solve my problem:

[STAThread]
public static void Main() {

// THE FOLLOWING LINE WAS ADD BY ME
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", false);
// END
instance.Run();
}

Now it works for me too. But I would like to use any culture I could need in the future.

So, my question is: Is there a way to make this solution runs without having to set the current culture to en-US?? For my project, it should be "fr-CA". I'm new in .NET, so I'm confused with all that. Confused

Thanks in advance and "Bravo" again Wink

Thanks to babel fish translator too, haha!

Simon
Generalnested workitems
thebts
16:28 5 Dec '06  
Any idea how you would cope with nested workitems in this framework?

GeneralUser Interface Element Insert Priority Coming Soon!
Tom Polanski
6:08 21 Mar '06  
User interface element (UIElement) insert priority is coming soon as an update to the module composite mapper service! Insert priority is set within the XML configuration file specified with a module and provides an end-user control over the ordering of UIElements that are added to the shell through the UI extension site composite mechanism (e.g., menu/toolbar item order independent of module load order). The feature is complete and will be covered in the article (as well as the downloadable source files) ASAP... thanks!

Tom Polanski, Lead Software Engineer
Xtensible Solutions
GeneralRe: User Interface Element Insert Priority Now Available!
Tom Polanski
17:04 30 Mar '06  
User Interface Element (UIElement) insert priority is now available including article coverage and the downloadable source files. The XSShell application module composite mapper file may be used to try out the insert priority functionality against the core UI menu items (i.e., File, Edit, View, Tools, Window and Help).

Tom Polanski, Lead Software Engineer
Xtensible Solutions
GeneralMyServerModule Dynamically Loads MyClientModule using Hardcoded Path
Tom Polanski
6:09 20 Mar '06  
As mentioned in the article, the MyClientModule is not loaded by default upon executing the XSShell application. The MyServerModule provides a user interface on the shell that dynamically loads the MyClientModule during application run-time. The MyServerModule.MyServerWorkItem.LoadClientModule method uses the following hardcoded path for the module loader service (this logic is not particularly smart with no error handling if the client module cannot be found or is already loaded... though, the Load Client user interface element on the standard tool menu is disabled after its first invocation):

@"C:\XSModuleCompMppr\MyClientModule\bin\Release\XtensibleSolutions.MyClientModule.dll"

One must locate the MyClientModule assembly per the path above in order for it to be dynamically loaded by the MyServerModule (assuming a local C drive, this is the default location on downloading and unzipping the project structure as is from this CodeProject article). Additionally, given that the POC framework shell uses the native CAB reflection-based module loader service against its local startup directory, one may circumvent the dynamic loading mechanism altogether by locating the MyClientModule assembly (XtensibleSolutions.MyClientModule.dll) within the shell executable directory (C:\XSModuleCompMppr\XSShell\bin\Release). Of course, much more robust logic for dynamic loading should be in place for production code.

Tom Polanski, Lead Software Engineer
Xtensible Solutions
GeneralBrilliant!
gcadmes
7:33 17 Mar '06  
Thank you for sharing your POC extension. I have found this resource to be invaluable! I hope to expand on this and share as well.

Greg Cadmes
GeneralAB's seem interesting
Peter Hayward
16:51 27 Feb '06  
Yes these application blocks in the Enterprise library sure do look interesting. Your article looks good, hopefully I'll a chance to really look through it in more detail.

Peter Hayward

GeneralExcellent
oykica
14:19 27 Feb '06  
Thanks for sharing your work. I really enjoy reading articles on how others are extending or using application blocks.


Last Updated 30 Mar 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010