Events from Child WorkItems

Topics: CAB & Smart Client Software Factory
May 14, 2007 at 7:22 AM
I have a use-case which as part of its functionality requires a child use-case -

As an example, let's suppose I have a Customer Details Edit screen, and on that screen I can find the spouse of the partner.
The "find spouse" is really a sub workitem which lets me find another Customer.

The Customer Edit screen creates a new ControlledWorkItem<FindCustomer>... and here's where my questions begin!!!

1 - What is the best way to display the FindCustomer views - should the FindCustomerController offer a Run overload which takes a Workspace, or should it expose view properties? My current thinking is that there should be a Run(IWorkspace workspace) method on the Controller and the controller should be in control of those views.

2 - When the user selects a customer in the FindCustomer use-case how, as a parent WorkItem, can I listen for this? CAB events are workitem scoped, global scoped, or descendent scoped, but not parent scoped. Should the FindCustomerController be offering a .net event which the parent work item can listen to?

With (2) I'm lost. I've been trying to think of doing things in terms of CAB events. My general thinking is that if something doesn't appear possible with CAB, maybe I'm trying to do it in a way which wasn't intended!

Any help would really be appreciated.

Thanks,
Graeme

May 14, 2007 at 10:37 AM
I guess that can be solved by using interface. In the view have an interface property, which the view will use to call appropriate methods on certain events. And let you parent work item implement that interface and assigning itself to the view's property. This you will receive calls in parent workitem without using .NET events.

public interface IController
{
void OnAddFolderNode(string nodeId);
void OnAddNode(string nodeId);
}

public class PassiveView
{
private IController controller;
...
public IController Controller{
get{ return controller; }
set{ controller = value; }
...
//On button click
private void AddFolderButton_Click(object sender, EventArgs e)
{
if(controller != null)
Controller.OnAddFolderNode(textBox.Text);
}
}

public class TreePresenter : IController
{
....
PassiveView view ;

public TreePresenter(){
view = new PassvieView();
view.Controller = this;
}
...
}
May 14, 2007 at 2:11 PM
This couples the parent work item pretty tightly to the child one though doesn't it?

Especially if the child is used in a few places then I suddenly have a bunch of consumers who are tightly coupled to the child work item.

CAB events seem perfect here as all I know is something will raise an event named 'CustomerSelected' but I don't need to know any more details of where it came from.
May 14, 2007 at 5:21 PM

graemefoster wrote:

The Customer Edit screen creates a new ControlledWorkItem<FindCustomer>... and here's where my questions begin!!!


That would be where my questions would begin too. Where I work, we don't allow Views/Presenters to create new WorkItems; I think if we did it would cause a lot of confusion. Now, maybe that's not what you meant, but it is what you wrote.


1 - What is the best way to display the FindCustomer views - should the FindCustomerController offer a Run overload which takes a Workspace, or should it expose view properties? My current thinking is that there should be a Run(IWorkspace workspace) method on the Controller and the controller should be in control of those views.


How much work is your FindCustomerController doing? Can that work be done in the Presenter for the FindCustomer view? I think part of the problem here is that you've designed this "use case" to require a workItemController (child workitem) when it might not be necessary. If you need to show the FindCustomer view in several different places in your application, then think about containing all of the necessary logic in the Presenter class for that view. There can't be much to it. Then you can create and display that view anywhere you like, in any workspace you like. So in one use case you might need to display it in a WindowWorkspace and in another use case you might need to display it in a zoneworkspace. If you were to redesign this solution a bit then you wouldn't need to instantiate a child workitem nor pass that workItem a Workspace. You could just create the view and interact with it via the presenter, using services and events.


2 - When the user selects a customer in the FindCustomer use-case how, as a parent WorkItem, can I listen for this? CAB events are workitem scoped, global scoped, or descendent scoped, but not parent scoped. Should the FindCustomerController be offering a .net event which the parent work item can listen to?


This is where I think your current design has hindered you the most. Think about what might be possible if your FindCustomer logic was contained in the presenter for the FindCustomer view. Whatever workitem is managing the EditCustomer view could then create the FindCustomer view when necessary and display it wherever it needs to. You could use a WorkItem scoped eventBroker event in the FindCustomer presenter, and the workItem managing the Editcustomer view and Findcustomer view should be able to respond to it.

I think the thing that this problem illustrates is to remember that in CAB a Presenter is perfectly capable of being the recipient of dependency injection, and it can listen to events and respond to commands where necessary. As such, a View/Presenter pair can be created to handle a specific "use case", and those two objects can become a powerful, reusable pair for handling some specific functionality. Then you can make use of the View/Presenter pair in various WorkItems that may want to use them in slightly different ways.



May 14, 2007 at 5:30 PM
I agree with Chris completely on this. I have a view that has a "Find" button it so that it can populate a list of items.

When the user hits the find it sends a message to the presenter to display this find. The presenter creates a window workspace (we wanted this) and displays the find view in it. When they are done they close the view and the results are sent back using the state object (State"ItemsList" = foundItems). The presenter that is associated with the main list view listens to the ChangedStateEvent and updates the view accordingly.

This works really well. Also in the find view it uses a service to retrieve the data it is searching for and then disposes of it when done to free the resources.

My 2 cents.
Jordon
May 15, 2007 at 12:53 AM
Edited May 15, 2007 at 1:15 AM
Thanks for the responses guys (Western Australia time really sucks for using forums! You end up with hours between posts)

Just to clear a couple of things up:

1 - Chris - you're correct. The Edit Customer view / presenter doesn't create the Find Customer WorkItem. It's the EditCustomer WorkItemController which does this. (Just out of interest would you let a Presenter create another SmartPart? This is something that the FindCustomerController does in the BankBranchWorkbench reference application)

2 - I've broken the Find Customer into its own WorkItem / Controller because it combines 2 views together (criteria and results). Should a combination of a couple of SmartParts necessitate a Controller. In this particular case all the Controller does is add the 2 smart parts. It doesn't subscribe to or publish any events.

The working example I currently have loses this controller as Chris describes. The EditCustomer WorkItem adds the FindCustomer Smart Parts directly into its own collection. The FindCustomerCriteria presenter does the find and raises a ResultsReturned event. The CustomerList SmartPart's presenter subscribes to this event and shows the list. And finally the CustomerList Presenter raises a 'CustomerSelected' event which is subscribed to by the EditCustomer presenter.

This does work really nicely but I got concerned by dropping the additional controller. My thinking was that Use-Case = needs a Controller but I'm starting to realise that that isn't necessarily the case.


May 30, 2007 at 3:49 PM
Hi, Graemefoster,

Your approach seems ok except last parts. Why do you add "FindCustomer " to smart parts of EditCustomer WorkItem. Rember the FindCustomer is a separate WorkItem/Usecase. You should not have to much interactions between them.

.NET Event is OK , (also the only solution, better than interface). My suggestions is to raise event such that the EditCustomer workItem can get the selected customer. That's enough, isn't it?

The key is to define the interface clearly so that each workItem is well encapsulated.