Design of the WorkItem
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies.
A WorkItem is a run-time container for components that are working together to fulfill a use case. These components may consist of SmartParts, controllers, services, UIElements, and other components.
You can use a WorkItem to encapsulate different use cases or areas of an application to share events and state. However, if you overuse WorkItems and make them too fine grained, or if you build your WorkItem classes inconsistently, you may end up with an application that is difficult to maintain.
The design of the Composite UI Application Block primarily supports applications built using a consistent set of patterns. One of the default patterns is the Model-View-Controller (MVC) pattern. Generally, these types of applications consist of the following:
- Initialization code
- State shared between components in the use case
- Controller classes that act on the state and other resources
- View classes that interact with their controller and read state
- Disposal code
Figure 1 shows how the WorkItem supports the other components and interactions that generally form the basis for a Composite UI Application Block application.
Composite UI Application Block application
In Figure 1, the steps are the following:
- The OnRunStarted method of the WorkItem is called when the application shell or user code starts the WorkItem running.
- The WorkItem can access data through the normal data access layers, process, or services external to the application
- The OnRunStarted method then shows the UI. State for the application is stored within the context of the WorkItem, possibly using externally defined business entities, classes, or definitions.
- The OnRunStarted method can automatically show any SmartParts referenced by the WorkItem, or access SmartParts already placed in containers on the form.
- In the Model-View-Controller (MVC) pattern, which is the common pattern for a CAB application, the SmartPart interacts with and passes on commands to a Controller class instance. In the Model-View-Presenter pattern (MVP), this will be a Presenter class instance.
- The Controller (or Presenter) interacts with, and takes advantage of the business logic and processes within the WorkItem.
- This business logic and other code can interact with the State stored within the WorkItem to perform the actions requested by the user. Depending on the pattern in use, the WorkItem can then raise events to cause the view of the data in the SmartPart to be updated.
WorkItems are also a good way to implement the Application Controller pattern. A WorkItem will contain the views, controllers, and other components in a sub-area of an application. Typically, you will create your own class derived from WorkItem and add your own methods. By default, the WorkItem class provides a Run method that causes the protected and virtual OnRunStarted method to execute, and fires the RunStarted event. You can implement other logic in the WorkItem by over-riding the OnRunStarted method.
There is always an active WorkItem, which implements the currently active use case, and you can activate and deactivate WorkItems as required. For example, a user may be in the process of entering a customer order when he or she needs to update that customer's details. The application can support the deactivation of the order use case and activate the customer details use case. When activating a SmartPart in a WorkItem, that WorkItem is automatically activated and any other WorkItem that was previously active is deactivated. The WorkItem exposes a series of events such as Activating, Activated, Deactivating, Deactivated, Terminating, and Terminated that you can use to track and enable the cancellation of the activation and deactivation processes. You can also replace the IWorkItemActivationService to provide a different behavior for this process.
A WorkItem also allows its child components to obtain references to other components in the same WorkItem using declarative attributes. This enables the components to interact with each other.
A WorkItem has a State property, which enables all the components in that WorkItem to access shared state. The property provides access to a loosely typed collection of elements identified by name. The collection can hold any type of object data. Modifying data in the State property raises the StateChanged event. The State property of a WorkItem is the only part of the WorkItem saved when you save the WorkItem.
When writing code that uses a WorkItem, you can iterate through that WorkItem instance to find components that you need. This WorkItem may have created some of these components, while an ancestor WorkItem may have created and registered others. Conversely, by adding components to your WorkItem, you also make them available to any child WorkItem. Specifically, the components that the WorkItem class percolates through to its children are the following:
- Shell elements, such as Workspaces, UIExtensionSites, and Commands
You can think of these components or objects as "capabilities." Some WorkItems may require a certain capability to be present before they execute (for example, a Workspace with a certain name or a specific infrastructure service). As WorkItems execute, they may also increase the capabilities available to themselves and their children—for example, by adding a Command. These types of objects added by the child WorkItem are not available to ancestor WorkItems; they are disposed along with the WorkItem that created them.
The Composite UI Application Block provides a mechanism for extending the behavior or capabilities of a WorkItem. A WorkItem extension allows you to write code that will run during the crucial events of WorkItem life cycle (for example, Initialized, Activated, Deactivated, or Terminating) without changes being required to the code of the WorkItem. The CAB automatically creates instances of these extensions and calls their methods, without requiring any code to connect them to the WorkItem.
You should use WorkItemExtensions in the following situations:
- You want to implement specific behavior in response to the events of one or more WorkItems, without changing the WorkItem code. For example, to record auditing or journal data for the start and stop times of all WorkItems, you could write the appropriate code in just one class and add an attribute to it that specifies it will be used with any WorkItem in the application.
- You want to add behavior to a specific WorkItem type. For example, you could extend a WorkItem that shows customer details through a WorkItemExtension that shows a new view when the WorkItem starts (as demonstrated by the BankTeller QuickStart example). You create a WorkItemExtension class, add an attribute that specifies the WorkItem that shows the customer details as the concrete type to extend, and then write the code to show the new view in the OnRunStartedmethod.
WorkItemExtensions can reside in a different module than the WorkItem they are extending, allowing implementation of very rich extensibility scenarios.