Sunday, May 06, 2007

CAB / Smart Client Software Factory pointers

I've been involved in a project which is using Microsofts Component Application Block for a few months now and thought I'd drop an entry on CAB 101 - do's and donots!

CAB is heavily based around MVP and the idea of splitting use-cases into a WorkItem. Sounds complicated? Don't let it. A WorkItem is nothing more than a container of things which are required to get the job of a use-case done.

So, a workitem may contain some views, some services, and some logic for co-ordinating them together. Simple huh?

Lets look at a use case of searching for customers.

What views are involved in this? For this example lets say we have a view to enter search text, and a view to show a list of customers.

Get your GUI guys to knock up these 2 views. Make them look blinding! Just hold off on the actual guts of what is going on as it's not their job!

GUI guys working on the views? Great. Let's begin the meaty stuff.

Lets create a class that will hold all of the parts involved in the use-case together. This is will be subclass of WorkItemController.

Override the Run() method on this type. Lets have it create a Search Criteria view and add it as SmartParts to the WorkItem's SmartPart collection.

SearchCriteriaView view = WorkItem.SmartParts.AddNew();

Let's also show this view in the main workspace.

Assume that the view has some fields for entering search criteria, and a Search button. When the user clicks on the Search button we'll delegate to our SearchCriteriaViewPresenter class (remember MVP)...

The Presenter for the Search Criteria screen is the thing that will go and find the results. But how does it do this?

This is where we use another piece of CAB goodness - its DependencyInjection framework. CAB will wire up objects we want, setting properties on them by magic. Did I mention that we have already asked CAB to register a CustomerSearch service on our behalf? Nope? Well we did!

In the Search Criteria presenter we'll have some lines that look like this...

public ICustomerSearch
set { _customerSearch = value; }

public void Search(....)
IList<SearchViewCustomer> customers = _customerSearch.Search(....)

But how do we get these results to the results view? This is where CAB's idea of loose coupling comes into play. What we'll do is add the results into the WorkItem's Items Collection and then raise an event to say we've found some search results. The Search method becomes more like this.

[EventPublication("ResultsReturned", PublicationScope.WorkItem)]
public event EventHandler<EventArgs> ResultsReturned;

public void Search(....)
IList<SearchViewCustomer> customers = _customerSearch.Search(....)
WorkItem.Items.Add(customers, "CustomerList");
ResultsReturned(this, EventArgs.Empty);

Woah! What's going on there. Well the EventPublication line instructs CAB that this class can raise a CAB event. CAB events offer more flexibility over .net events. We can change the scope of who they are raised to. In our case we're going to tell the WorkItem that we've found some results.

Go back to the WorkItemController from earlier and add the following method.

public void OnResultsReturned(object sender, EventArgs e)
ResultsView view = WorkItem.SmartParts.AddNew();

Remember that other gui screen that was being prepared? Get into the Presenter and add a constructor.

public ResultsViewPresenter(
[ComponentDependency("CustomerList") IList<Customer> customers] )
View.Customers = customers;

The InjectionConstructor attribute is a Object Builder attribute that will let us easily inject data into the new Presenter. In this case we are telling builder that the constructor is dependent on an item called CustomerList. It will look for this item in the Items collection and pass it to the constructor for us.

All we have to do is bind the customers list to the view.

Recap! What have we achieved?

We've created 2 smart parts which act together to perform the use case of finding customers.
W've created a WorkItemController which will hold these 2 views giving us a context for working with just the 2 views loosely without them knowing about each other.
We've used a CAB Event to tell anyone interested that we found some customers.
We used CAB's Items collection to share the customers between 2 views without either of them being aware of each other.
And we used the dependency injection system to obtain a reference to a service, and to wire up a Presenter.

Not bad for 10 minutes work!

1 comment:

SonnyMal said...

Great article,

I've been working with the web client software factory which also relies on MVP and policy injection.

As a general question: In theory shouldn't the internal workings be the same for the smart and web software factories?

I mean the purpose of MVP and policy injection(aspect object programming) is so that there is loose coupling between the work item (controller logic) and the U.I. If so why would there be two software factories (reason I ask you is that i have little experience with smart client software factory).

I guess it is more related to the packaging of the software factories -CAB,Web.

The recipes must be different but then again it would have been easier to just bundle them into one as the recipes wouldn't conflict.