CAB Smart Clients in an Agile World Part 2
So I was going to show how to tame this CAB beast by sharing what I learned on that “CAB Spike” about a month and 1/2 ago. Again, from Part 1 of this series, the Spike is when you have no clue what you are doing, have to leave the schedule of User Stories to go investigate some technical subject, understand it, and minimise risk to the project! So I combined one of the CAB walk-thrus during the Spike with our code base to see if I could up with an Application Shell. Lets call our company Acme to protect the guilty.
For me, CAB starts with the Module. What is a Module? Microsoft says, “One of the key goals of the Composite UI Application Block is to support the development of applications by using independent, but collaborating, modules.
The Composite UI Application Block promotes modularity by allowing you to implement business logic, visual SmartParts, infrastructure components, presenter or controller components, and any other objects the application requires, in separate modules.” CAB then does a lot of cool “wiring” type things underneath for you like placing SmartParts into workspaces, components to publish and subscribe to events without having to know about each other, allow components to share state and information by placing it into the State property of the WorkItem and many more. So the Module is the dynamic unit of stuff that CAB loads up that does a distinct composable unit of the UI.
So the first thing I did is create a WinForms project for the Application Shell and created a custom Work Item:
using
Microsoft.Practices.CompositeUI;
public class AcmeShellApplication : FormShellApplication
AcmeShellForm> {
}
[STAThread]
static void Main() {
new AcmeShellApplication().Run();
}
public class AcmeShellWorkItem : WorkItem {
}
he root of the non-display stuff in CAB is our class AcmeShellApplication,, which derives from the class FormShellApplication in the namespace Microsoft.Practices.CompositeUI.WinForms. This class derives from other base classes, namely WindowsFormsApplication, CabShellApplication and CabApplication. The AcmeShellApplication derived class, known as the application class, creates the root WorkItem, which contains the services and child WorkItems within the application.
The next thing to do is get CAB to load your module. CAB creates dynamic composable UIs by reading the list of Modules from, what else? An XML file of course! In this case, it reads from a file called ProfileCatalog.xml. This work is done in our derived ModuleInit class. The code looks like the following in the AcmeModuleInit.cs file:
// Initial : Sam Gentile
// ********************************************************************************
using System;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.Services;
using System.ComponentModel;
namespace Acme.Collateral.GUI.Core {
/// When it starts, the CAB automatically instantiates any class that inherits from the ModuleInit class. We are adding a AcmeModuleInit class to our AcmeModule project so that the module can register its WorkItem when the application starts.///
public class AcmeModuleInit : ModuleInit {
private IWorkItemTypeCatalogService acmeCatalogService;
private WorkItem parentWorkItem;
[ServiceDependency]
public WorkItem ParentWorkItem {
set { parentWorkItem = value; }
}
/// Use the [ServiceDependency] attribute on this property so that the dependency injection feature of the underlying ObjectBuilder utility will create an instance of the service and pass back a reference to it:
[ServiceDependency]
public IWorkItemTypeCatalogService AcmeCatalogService {
set { this.acmeCatalogService = value; }
}
public override void Load() {
base.Load();
AcmeWorkItem acmeWorkItem = parentWorkItem.WorkItems.AddNew<AcmeWorkItem>();
acmeCatalogService.RegisterWorkItem<AcmeWorkItem>();
acmeWorkItem.Run(parentWorkItem.Workspaces["tabWorkspace1"]);
}
}
}
The really cool thing here is the [ServiceDependency] attribute which uses the Dependency Injection pattern so that the WorkItem and the Catalog Service get automatically created. The Catalog Service is one of the services that CAB provides to read the modules from the Profile Catalog and load them. We then register our WorkItem with CAB and then we call Run on it telling it to place in a Workspace called “tabWorkspace1”.
WorkItems are a very interesting thing. The CAB docs say all this stuff of how they are the encapsulation of one use case scenario in your UI with everything related to that (Model, View, Presenters, Controls, Smart Parts, etc). So you may have a WorkItem that deals with Importing a file in your UI or to Print or to brew coffee-). But the interesting point brought up here in Redmond yesterday in the SCBAT workshop is that they are not really that. They are really Containers, nothing more, nothing less. They can be used to encapsulate and fulfill a Use Case but that’s not what they are. WorkItems You can use a WorkItem to encapsulate different use cases or areas of an application to share events and state. CAB also has a State service and an Event Broker system for events, among others. You can scope state and events to a WorkItem or globally. A WorkItem really bcomes a Container for implementing MVC or MVP.
Once we have registered the WorkItem with CAB, we call its run method, passing the Workspace of the parent WorkItem. Workspaces are components that encapsulate a particular visual way of displaying controls and SmartParts. They are like a canvas to project stuff into visually and lay it out. CAB ships with 5 pre-built Workspaces out of the box. We’ll look at this next in Part 3.
Now playing: The Rolling Stones - A Bigger Bang - She Saw Me Coming
Now playing: The Rolling Stones - A Bigger Bang - This Place Is Empty