Composite UI Application Block: Modules explained


I hope my article will at least add to common understanding on this otherwise so poorly covered theme.

Last week I was crunching module loading for a product of ours requiring dynamically replaceable customer specific (business) module. There are dozens of poor variations on GPSModule Quick Start from original Microsoft Composite UI Application Block package all poorly constructed and even poorer explained. The answers I was looking for I found in this excellent blog of Rich Newman where I found out how exactly does module initialization work.

Generally you do three things

1. Move relevant code into separate project (that will later be compiled into separate assembly);
2. Implement class specializing Microsoft.Practices.CompositeUI.ModuleInit. This class will provide entry point for module loading where you will do initialization, loading and binding events for example;
3. Add to shell application ProfileCatalog.xml that will define what modules to load

Simple huh? Well not really. There are number of dodgy things about loading modules which are not related to module loading itself but rather to how you properly initialize business related code within module. In particular I’d like to touch two themes…

I will use this code as an example:

public class CustomerListModuleInit : ModuleInit
{
private WorkItem parentWorkItem;

[ServiceDependency]
public WorkItem ParentWorkItem
{
set { parentWorkItem = value; }
}
[EventSubscription(“event://MyApplication/Administration/DisplayCustomerList”)]
public void DisplayCustomerList(object sender, EventArgs e)
{
if (parentWorkItem.RootWorkItem is MyApplicationWorkItem)
{
WorkItem customerListWorkITem = parentWorkItem.RootWorkItem.WorkItems
.AddNew<CustomerListWorkItem>();
List<IPresenter> presenters = customerListWorkItem.GetPresenters();
presenters.ForEach(delegate(IPresenter presenter)
{
customerListWorkItem.Services.Get<IWorkspaceLocatorService>();
IWorkspace wks = locator.FindContainingWorkspace(customerListWorkITem, presenter.View);
if (wks != null) wks.Show(presenter.View);
});
}
}
}

Access to Workitem during module initialization

Because property ParentWorkItem has a [ServiceDependency] attribute it is clear that it will be injected by means of service locator. But the true question is – what ParentWorkItem? The answer is that the WorkItem that is passed is the WorkItem that caused the module to be loaded. We can clearly see it in LoadModules() method of CabApplication that in turn is is called directlly in Run() method of CabApplication.

private void LoadModules()
{
IModuleLoaderService loader = rootWorkItem.Services.Get<IModuleLoaderService>(true);
IModuleEnumerator modEnumerator = rootWorkItem.Services.Get<IModuleEnumerator>(true);
if (modEnumerator != null)
loader.Load(rootWorkItem, modEnumerator.EnumerateModules()); // ROOTWORKITEM !!!
}

As you can see first parameter of Load() method of loader is rootWorkItem in default application module loading algorithm. The reason why property is still called ParentWorkItem and not RootWorkItem is that in some cases if you do hocus pocus you could delegate loading of particular modules to any workitem. So to keep code error free in the class specializing ModuleInit I used parentWorkItem.RootWorkItem notation. This allows me to ensure that I always have reference to the RootWorkItem even in the cases where ParentWorkItem injected by service locator actually is one of child work items.

Event subscriptions

Another interesting thing about module loading is the way one can wire-up events. I mean, in some typical application of yours you might have shell application with some basic (or if you like cross-cutting) stuff in it such as user administration, master layouts, main menu or main page, etc. The actual business logic of your application might be implemented in different modules such as customer management, product catalogue and invoicing. Now, let us assume you would like to move your customer management into a separate module. You do that by performing three steps I described above. After you’ve done this it is time to wire-up your freshly created module into main application. Typically, you’d have for example main menu item for showing customer list. When user clicks on this main menu an event is fired. For example like this:

[EventPublication("event://MyApplication/Administration/DisplayCustomerList")]
public event EventHandler<EventArgs> DisplayCustomerList;

Now, you would like to handle this event in your customer management module and upon firing of event, create customer list work item that would do the job of showing application user that list of customers.

It took me some time to understand where to actually place event handler (or in terms of CAB – EventSubscription) that will handle published event, create customer list work item and run it. The way I did it is shown in code of CustomerListModuleInit class above:

public class CustomerListModuleInit : ModuleInit
{
...
[EventSubscription("event://MyApplication/Administration/DisplayCustomerList")]
public void DisplayCustomerList(object sender, EventArgs e)
...
}

Why like this? Well, for starters EventSubscription method must be in a class that is instantiated when event is fired. RootWorkItem itself can not have this method since it has no reference to CustomerListWorkItem which is located in a module. To instantiate CustomerListWorkItem upon module loading makes no sense as well, since there is no guarantee that application user will use it at all in current work session. Thus the only logical place that remains is – CustomerListModuleInit that is instantiated unconditionally and per definition should contain code that ensures module wire-up with main application. As soon as I realized this, the implementation became obvious 🙂