The composite UI Application Block (called "CAB" in the online community) is a
new application block that is being developed by Microsoft patterns &
practices and is scheduled for release shortly after the final release of .NET
2.0. The current code for the Composite UI Application Block is the first
Community Technology Preview. As with any beta release, there could always be
changes before the final version. The core design and concepts, however, are
less likely to undergo any major changes, and it is worth taking a look at the
architecture and design of the block for several reasons:
- To see a "real-life" implementation of some interesting design patterns in
accordance with Microsoft's own standards - patterns that could be useful in
one's own work - To evaluate the Application Block to determine if it
(or portions of it) would be useful in an upcoming Windows Forms project - To get a glimpse of some of Microsoft patterns & practices'
guidance for using some of the new features of .NET 2.0
What is CAB?CAB is an architectural framework that provides a flexible and decoupled approach for developing Windows Forms applications. It employs proven design patterns in a set of base classes and services that allow for construction of controls and forms and that can be used to control UI navigation, workflow, and application state. The CAB UI framework is designed to improve the development speed, ease of maintenance, reliability, and reusability in large Windows Forms applications.
In addition, CAB is built on a new component-based framework that is an extension of the System.ComponentModel namespace. This framework is based at its core on the Dependency Injection/Inversion of Control design pattern. This has the potential to give it immense flexibility and power; I will explore this later in this article. Along with the Dependency Injection implementation, CAB employs some of the new features of .NET 2.0, such as generics.
Background - Application Blocks and the Enterprise LibraryThe patterns & practices group at Microsoft provides guidance for the use of Microsoft technologies, as well as general software architecture and development patterns. Part of the way the patterns & practices group does this is to produce and distribute source code that has been developed using the best practices of Microsoft architecture. This code is distributed at no charge. The source code packages that are not full reference implementations are called application blocks.
The first application blocks came out several years ago and were released individually. They provided a great reference for developers, giving them a view into the best practices of Microsoft-produced source code and application architecture. Some of them provided a set of core components that could be used in almost any type of application to simplify common tasks such as database access, exception handling, logging, and encryption. Others provided design and architectural guidance to address complex design problems using established design patterns.
Since the first set of Application Blocks were developed one at a time and by separate teams, they had different core patterns, organization, and naming standards and were developed with different architectural approaches from one another. To provide a consistent approach, Microsoft released the "Enterprise Library" in late 2004 - this is a set of the most widely employed Application Blocks, re-engineered using consistent coding standards, patterns and architecture, and using the Configuration Application Block to handle reading and writing of all configuration settings.
The User Interface Process Application Block and the Composite UI Application Block (CAB)One of the original Application Blocks that was not included in the Enterprise Library was the "User Interface Process Application Block." As those of you who used or read about this block know, the architectural concepts around which it was designed were solid, but the implementation worked better in some applications than in others. It made an attempt to abstract all User Interfaces and to control Web and Windows Forms navigation using the same approach, and the consensus seemed to be that it was better suited for Windows applications, primarily due to the greater control that Windows Forms give the developer over state when navigating backwards.
The Composite UI Application Block takes up the same goals as the User Interface Process Application Block, and this time is geared specifically for Windows Forms applications in .NET 2.0 (there will be a separate Application Block for ASP.NET Composite UI). It is the first application block that is being designed exclusively for .NET 2.0, and it is the first one to be rolled out since the introduction of the Enterprise Library.
Why isn't CAB in the Enterprise Library?The Enterprise Library application blocks are basically sets of service provider classes. These classes provide simple interfaces for developers to access common, shared services to perform tasks such as checking security, connecting to and running commands against a database, logging, and loading/saving configuration information. The providers are all simple and straightforward to use. They support complex configuration options and flexibility in how they do their work, and they are designed to allow developers to easily add custom providers.
What the Enterprise Library does NOT provide is an overall architectural approach. The Enterprise Library Application Blocks are self-contained, and they can be integrated into virtually any architecture due to their flexibility. The User Interface Process Application Block and CAB, on the other hand, are architectures, at least for the User Interface layer of an application. They really drive how the User Interface works at a fundamental level.
For example, part of the package released with CAB defines a set of base container classes. In an application built using CAB, all objects that participate in the UI are created and exist inside these containers. Other CAB-defined classes control the layout of the interface. Controllers that are defined in the CAB architecture manage user workflow, and CAB handles storage and retrieval of state between views.
Basic Concepts - CAB and Component ModelThe CAB core, as of the first Community Technology Preview, consists of three projects. The first two, CompositeUI and CompositeUI.WinForms, compose the UI-centric portion of the code. The third, ComponentModel, contains the structural classes that make up the backbone of the architecture.
Composite UI - Modeling the Use CaseOne of the central concepts in CAB is the WorkItem. Each WorkItem represents a single use case. A WorkItem object is a container that holds all of the objects required to complete the use case for which it is responsible. These objects could be user controls (called "SmartParts" in CAB), state containers, or even other WorkItems to handle child processes.
SmartParts are the windows controls that you use to build the UI for an application. They are "smart" because they live within the context of a WorkItem and are self-describing. These can be any type of control, including custom-developed user controls. All you have to do to make a control a SmartPart is to mark the class with the [SmartPart] attribute.
WorkItems may contain state. The State class is essentially a name/object collection that can be used to store any state data that needs to be maintained during the life of the WorkItem and that needs to be available for any of the SmartParts contained inside that WorkItem.
WorkItems and their child SmartParts must live in some sort of UI shell. CAB provides base classes on which to build a number of different types of Windows application interfaces, which are called "workspaces." The current implementation gives you the following workspace types:
· Window - a standard window
· MDI - Multiple Document Interface
· Tab - A window with tabs to switch between views
· Deck - Similar to a deck of cards, SmartParts are layered on top of one another and can be opened, closed, and/or reordered by the controller
· Zone - A tiled interface like Microsoft Outlook, with multiple zones that can interact with one another
To make it all work together, Controllers are defined for each View (Form or SmartPart) that interprets the state and user input when a user takes action to determine what the application should do next.
ComponentModelWhile the primary visible deliverables of CAB are related to Windows Forms and the Model/View/Controller pattern, much of the value of CAB is in its implementation of the Dependency Injection design pattern in the Microsoft.Practices.ComponentModel namespace. The Microsoft.Practices.ComponentModel namespace is declared outside the Microsoft.ApplicationBlocks namespace because it was designed to be available for use in many patterns & practices releases in the future, including non-Application Block releases. The classes in this namespace form a component architecture based on the Dependency Injection/Inversion of Control design pattern and Dependency Injection containers.
Dependency Injection is a pattern that was originally developed in the Java community and represents a specific application of the Inversion of Control pattern. The definition given in the CAB documentation is, "A pattern that enables reuse and loose coupling of components through the injection of component dependencies at run time." For an in-depth introduction to Dependency Injection, take a look at Martin Fowler's article at www.martinfowler.com/articles/injection.html. The basic idea of Dependency Injection is to design your software to work with objects that are as self-contained as possible and have very basic, generic interfaces, and to determine the details of what each object really is at run time based on the needs of the specific implementation.
The best way to illustrate how CAB uses Dependency Injection is with an example that uses WorkItems. A WorkItem is defined to require a specific set of SmartParts to do its job (complete its use case). This example consists of a use case for a billing system that states that Customer Service users will modify customer profile records (see Figure).
The WorkItem that models this use case requires a SmartPart that displays a form for entering customer information in the UI.
Based on the requirements for this billing system, the SmartPart used to enter customer information differs significantly for different types of customers. An industrial customer, for instance, uses a significantly different set of controls and fields than a government customer does. In addition, new types of customers are added all the time, and much of the time a new type of customer means a new SmartPart must be developed and added to the UI.
CAB handles this type of situation easily, at least from the UI perspective, using Dependency Injection. All the WorkItem knows about or cares about is that it must have an "EnterCustomerInfo" SmartPart inside it - this is its "dependency." The specific SmartPart that will be inserted into the WorkItem by the application (and subsequently displayed in the UI) will be determined by the type of customer. The mapping of the generic container to the specific object can be done either in code or in configuration. The insertion of the specific implementation of the SmartPart into the generic, reusable "Create New Customer Record" WorkItem is an example of Dependency Injection.
Publish/Subscribe EventsAnother powerful pattern implementation is the Publish/Subscribe events. Essentially, event handlers on one object are set up to handle events that take place on other objects. This can be very useful in complex user interfaces, and is essential in a loosely coupled architecture like CAB, where clicking on a control in one part of the UI must be able to have an effect on any number of other portions of the interface.
Objects that need to know when an event fires in another object will "subscribe" to that event. This is done in CAB by placing the "EventSubscription" attribute on an event handler method on each object that needs to know when a specific event is fired (C#):
[EventSubscription("event://EventName", EventScope.Global)]public void HandleExternalEvent(object sender, EventArgs args)
The events that are published will be intercepted by the EventBroker service and all subscribers will have their handlers fired and be passed the appropriate event arguments. Publishing is done by marking an event declaration with the EventPublication attribute as follows (C#):
[EventPublication("event://EventName", EventScope.Global)]public event MyGlobalEventHandler EventName;
Note that the specific event is defined in the attributes using the URI "event://EventName," and that in the examples above the EventScope is set to global. You can also set the EventScope to "WorkItem," which allows you to publish events that will only be handled by objects that are inside the same WorkItem container.
Application of GenericsCAB provides an immense amount of guidance in the form of examples of how to implement new .NET 2.0 features, such as generic typed collections. One particularly interesting implementation is the generic factory method overloads on the Microsoft.Practices.ComponentModel.CompositeContainer class.
All Dependency Injection Container classes inherit from this base class, which defines a bare-bones object that is a container itself and can also be a component in a parent container. This class has some methods defined that are "generic factories." The methods look like this (C#):
public T Create(string name) where T : new()
The "T" is a type placeholder. It represents the type of object that is being instantiated and returned by the factory method. It is both the return type, and also the type specified by the calling entity in the generic "typeparam" (the type name inside the "<>" characters when the method is called).
The "where T : new()" portion of the method declaration is a compiler constraint. It tells the compiler that whatever type "T" represents in any given call of this method, it must have a public constructor that takes no parameters.
This type of factory method provides an immense amount of decoupling - it's essentially an "everything factory." In the "CompositeContainer" implementation, this method creates the new object and then inserts it into its own internal generic Dictionary
SummaryI have really just brushed the surface of some of the content and guidance included in CAB - it has a really broad scope and provides a tremendous amount of reference material and useful pattern implementations. More content and functionality will be added to it, including UI process navigation abstraction similar to that provided by the UI Process Application Block, either when it is officially released or incrementally thereafter.
There is great potential to go along with the somewhat-daunting learning curve, however, and the ideas and concepts are creative and smart and well worth the study.
Resources
· CAB community Web site: http://practices.gotdotnet.com/projects/cab
· Microsoft patterns & practices: http://msdn.microsoft.com/practices/
· Martin Fowler on Dependency Injection: www.martinfowler.com/articles/injection.html