First lets see what are the basic elements of CAB and then lets try to discover them in the PetShop application. I will start with the parts that are actually visible on screen and later work my way to more abstract elements that form the inner architecture.
So the simplest of all things is the SmartPart. Basically this is any visible part of application; in other words a control. In most cases it would be a UserControl. What's surprising there is no special base class that you must inherit from. You only need to tag the declaration of such control with a [SmartPart] attribute. The trick is that these such parts should be independent from one-another as possible and communicate only through well known channels. This allows “composing” the application from many such parts and switching them dynamically.
But to be able to do so the application must know where are the placeholders that you want to put your controls in. In CAB these are called Workspaces. These are special controls that you can put on your Form or UserControl and then tell the framework to display a SmartPart in particular Workspace. Out of the box we get following types of workspaces:
- WindowWorkspace
- MdiWorkspace
- TabWorkspace
- DeckWorkspace
- ZoneWorkspace
There are also guidelines on how to build your own workspace. Each workspace implements set of methods and properties that allow to show, and control SmartParts. Related to workspaces are SmartPartInfo components that you can put on your SmartPart to provide additional information to the containing Workspace (for example to tell the TabWorkspace what should be the caption and position of the tab).
However all these elements can't live if you don't host them in a suitable environment which is provided by a Shell. It is a root of the non-visual organization of components and you create it by writing a class that derives from FormShellApplication. Actually this is a generic class which takes two types: a main Form and a shell WorkItem (more on this later). It also changes the way you start your application. Instead of the usual Application.Run(form) we have:
public class ShellApplication : FormShellApplication<WorkItem, ShellForm>
{
[STAThread]
public static void Main()
{
new ShellApplication.Run();
}
}
The Run() method initializes the whole framework including loading several standard services.
So now lets try to apply what we already learned to our sample application. By quickly looking at the current implementation of web UI it occurs that we can basically map all existing pages to corresponding SmartParts. The only issue is that all pages reference the same header control (and some also have a footer banner). I don't want to repeat this because what's the point of composite UI if you had to build every screen from scratch. Instead I've composed my ShellForm of three workspaces: header, content and footer. I guess if they had to create PetShop on current ASP.NET 2.0 they would do this in similar fashion using master pages.
Next I've created my first three SmartParts to recreate the contents of default page. Finally I had to override the AfterShellCreated method of the ShellApplication in order to place these SmartParts in their respective workspaces:
protected override void AfterShellCreated()
{
base.AfterShellCreated();
IWorkspace headerWorkspace = this.RootWorkItem.Workspaces["header"];
IWorkspace footerWorkspace = this.RootWorkItem.Workspaces["footer"];
IWorkspace contentWorkspace = this.RootWorkItem.Workspaces["content"];
contentWorkspace.Show(this.RootWorkItem.SmartParts.AddNew<DefaultPage>());
footerWorkspace.Show(this.RootWorkItem.SmartParts.AddNew<Banner>());
headerWorkspace.Show(this.RootWorkItem.SmartParts.AddNew<NavBarNoMenu>());
}
Below you can see how the application looks right now (doesn't look very impressive yet but I hope thats only a beginning ;-)
Original Image