Show another view from within a view

Topics: CAB & Smart Client Software Factory
Jul 21, 2007 at 6:20 AM
Hi all,

I've got a requirement to show a view where the user can enter and send email's (pretty much like Outlook's create messages).
Part of the functionality is for a user to be able to select recipients from their address book (not Outlook's but our own aplications).

I'm trying to think of the best way to handle this - firing an event from the Presenter which would be picked up in an address book's controller / presenter feels a bit loose to me as I know I will be getting control right back from the address book once the user has selected.

The ideas I've had so far are

1) The WorkItemController creates both View/Presenters. When the user clicks the "..." button to select email addresses the Presenter raises an event which is captured by the address book Presenter and causes the next view to show modally. When the user has selected email addresses, a new event is raised which is picked up by the original view. This doesn't feel like what the event pattern is trying to achieve to me.

2) I register the 'select email addresses view' as an ICoreAppServices service on the root work item. The WorkItemController only creates the 1st view, and when the '...' button is selected, the Presenter gets the ICoreAppServices and calls ICoreAppServices.ShowAddressBook() which returns an ICollection<EmailAddresses>.

Both approaches seem testable to me, but 2 stands out as the event pattern seems to loose - It relies on something listening to that event when in this case it is imperative that something is listening!

What do you all reckon is a good solution?

Thanks,

Graeme
Jul 24, 2007 at 8:44 AM
Edited Jul 24, 2007 at 10:21 AM
Anyone any ideas?

I think a better way to phase my question would be

What is the best way of showing another view, when you the two views are tightly coupled together and you know exactly where you are going to when you click on button 'x'.

If I have a generic customer details screen that can be shown in a variety of use-cases, and on that view is a button saying 'Work with customer' then I would use an event that could be picked up in the next View/Presenter (which would be different in each usage).

But if I have a button defined on a view, and I know exactly where to go to when that button is clicked, then an EventPublication seems overkill. If the presenters job is to manage the view, and the button is on the view, then shouldn't I be injecting an INextView into the presenter which is shown directly into an IWorkspace which is also injected into the presenter?

Or even have some sort of plug-in system when the WorkItem is created allowing me to poke button X via a UI Extension site, or service.

Or am I thinking way too much about this!?!

Thanks,

Graeme
Developer
Jul 24, 2007 at 7:39 PM
Edited Jul 24, 2007 at 7:48 PM
Hi Graeme,

If you have a button in a view and it’s clicked, in the button click event you should call to the presenter. Then in the presenter you should add the new view you want to show. Let me shows what I mean with an example:

View: CustomerDetailView. This is the button click event.

	
private void OnShowComments(object sender, EventArgs e)
{
    _presenter.ShowCustomerComments();
}
Presenter: CustomerDetailViewPresenter
	
public void ShowCustomerComments()
{
    CreateCommentsView();
 
    IWorkspace ws = WorkItem.Workspaces[WorkspaceNames.CustomerDetailTabWorkspace];
 
    if (ws != null)
    {
        ws.Show(commentsView);
    }
}
 
private void CreateCommentsView()
{
    commentsView = commentsView ?? WorkItem.Items.AddNew<CustomerCommentsView>();
    ISmartPartInfo info = new TabSmartPartInfo();
    info.Title = "Comments";
    WorkItem.RegisterSmartPartInfo(commentsView, info);
}
Note: this is an example from the SCSF BankTeller Quickstart of the SCSF Contrib project.

About event broker pattern, what you have to have in mind is that if you need immediate response or need exactly to know who a component is talking to, event broker is the wrong system. You should avoid implementing immediate response by raising event back and forward between components.

I recommend you reading the following post:

Please let me know if this helps,

Ezequiel Jadib
http://staff.southworks.net/blogs/ejadib
Jul 25, 2007 at 12:57 AM
Thanks for that Ezequiel,

What you haven't mentioned though is that the Presenter in the BankTeller quickstart has a very tight dependency to the real view.
Which means that you can't test the piece of code which would show the results screen if there were results.
Maybe that's fair enough - I guess it's borderline business logic at the best, creating and showing a view.

When I started tinkering with CAB I hit the idea that CAB events were a way to loosely couple things, which was great. I could update a customer and cause other views to update the details via. an event.

Then I hit a problem of how does a link on one view cause another view to display. I'm aiming for pretty high test coverage with my Presenters, so hit the idea of raising a CAB event which would be picked up by a listener somewhere which would show the next view. I could write a test to ensure than _presenter.ShowCustomerDetails() raised the correct event with the correct event args.

Then I realised that this wasn't right. In 99% of the cases I knew exactly what view should be shown so why raise the event. It was like I was heading down a road of using CAB to loosely couple my business processes which should be tight!
But I'd rather not tightly couple the view. If I do then I can't test that piece of code (I'm starting to think that maybe that's some code which I should let go untested). I've an idea of passing a ViewCreator<IViewB> via IoC which would let me use a MockViewB in testing but that feels a bit overkill.

Incidentally we're using the WPF CAB layer. CAB's event broker is slowly being relegated thanks to WPF data binding. If one View changes a customer, all the others get it courtesy of data binding. If two views show the same list then ObservableCollections at the agent layer cause both views to update themselves independently.

We're still using EventBroker to raise WorkItemCreated events to let other modules plug in via. UI Extensions but that's pretty much it at the moment. Is this a common theme with WPF Data Binding?

Thank,

Graeme
Jul 25, 2007 at 2:43 PM

What you haven't mentioned though is that the Presenter in the BankTeller quickstart has a very tight dependency to the real view.


Actually, the presenter only has a dependency on the Interface. The exact implementation of the View could be anything.


Which means that you can't test the piece of code which would show the results screen if there were results.
Maybe that's fair enough - I guess it's borderline business logic at the best, creating and showing a view.


Sure you can test that logic. I can think of two ways immediately.

The first way would be to refactor the code to use an EventBroker event. Now, this is just personal preference, but I don't like Presenters actually creating other views. So in our application we have WorkItems that control the behavior of creating and showing views. How my implementation would differe from Ezequiel's is like this:

[EventPublication(EventTopicNames.ShowCustomerComments, PublicationScope.Global)]
public event EventHandler  OnShowCustomerComments;
 
public void ShowCustomerComments()
{
   if(OnShowCustomerComments != null)
      OnShowCustomerComments(this, new EventArgs());
    
}

The WorkItem controlling this action (one level above in the object hierarchy) would be responsible for the code to create and show the view.

Another way to test it would simply be to check the Workspace for the proper view when the presenter creates it and shows it. I use DeckWorkspaces as faux workspaces when I test, because without a visual component they don't show anything on the screen during unit testing. A quick check of the ActiveSmartPart on the CustomerDetailTabWorkspace should tell you if it is the correct view type (checking against the interface) which would let you know the view got created and displayed.




Then I hit a problem of how does a link on one view cause another view to display.


I think this is where the concept of a controlling workitem for a given use case really helps. That workitem then has the responsibility to listen for events such as this and instantiate, show and close views when appropriate.


Then I realised that this wasn't right. In 99% of the cases I knew exactly what view should be shown so why raise the event. It was like I was heading down a road of using CAB to loosely couple my business processes which should be tight!


Too much concern about coupling. What you should be asking yourself is: Is this easy to test? Is this easy to follow the logic of the program flow? Are the responsibilities between my classes clearly defined? Will I be able to modify this design quickly and easily at a later point in time if something changes?


But I'd rather not tightly couple the view. If I do then I can't test that piece of code (I'm starting to think that maybe that's some code which I should let go untested). I've an idea of passing a ViewCreator<IViewB> via IoC which would let me use a MockViewB in testing but that feels a bit overkill.


We use a IViewFactory where I work to allow for the creation of view stubs during testing. The real IViewFactory has a very simple implementation:

public class ViewFactory : IViewFactory
    {
        public InterfaceType Create<ViewType, InterfaceType>(WorkItem workItem, string id) where ViewType : InterfaceType
        {
            return workItem.SmartParts.AddNew<ViewType, InterfaceType>(id);
        }
    }

The version we use in testing actually intercepts the ViewType and replaces it with a stub implementation. I don't find it overkill; on the contrary, it allows us to test everything.
Jul 26, 2007 at 1:39 AM

Actually, the presenter only has a dependency on the Interface. The exact implementation of the View could be anything.


No not the View that is being presented - the View that is being spawned via. the Presenter in the BankTeller reference implementation as in:

public class CustomerDetailViewPresenter: Presenter<ICustomerDetailView>
{ 
 ...
 
public void ShowCustomerComments()
{
    CreateCommentsView();
 
    IWorkspace ws = WorkItem.Workspaces[WorkspaceNames.CustomerDetailTabWorkspace];
 
    if (ws != null)
    {
        ws.Show(commentsView);
    }
}
private void CreateCommentsView()
{
    commentsView = commentsView ?? WorkItem.Items.AddNew<CustomerCommentsView>();
    ISmartPartInfo info = new TabSmartPartInfo();
    info.Title = "Comments";
    WorkItem.RegisterSmartPartInfo(commentsView, info);
}
 
}

When I test Presenter<ICustomerDetailView> I'm doing everything I can to isolate it - Mock services, MockCustomerDetailView. It's not possible to test the CreateCommentsView method though as it creates a new concrete CustomerCommentsView view as it could have dependencies on services not in my test, it could show message boxes. We just don't know.

Most of the latest piece of our application uses Events to initiate new WorkItems via Event Listeners in WorkItemControllers. For example, when we want to work with a deal we raise the WorkWithDeal event. This is great because the listener is the only thing which initiates the ModifyDeal use-case which means it will re-use an existing WorkItem or create a new one.

We have some UI ideas which don't really fit well with this approach though. For example, we have lists of deals which have toolbars allowing you to perform an action on a Deal even though the Deal use-case hasn't yet begun. The action currently is just calling a service but I could see in the future the User may need to do other things to get the deal in a correct state. I'm thinking of a change where clicking the 'Finalise' button on a list of deals causes the Deal WorkItem to spin up and passes control via it's Finalise command.

Thanks for the input guys - you should be charging consultancy rates for it!!!

Graeme
Aug 14, 2007 at 1:13 PM
Hello,
I was also interested by the question asked by Graeme about this basic navigation problem.
I agree that the implementation posted by ejadib, for me, is not correct because a presenter of view should not see an other view.

The implementation of Chirs using an event to communicate between the presenter and its controller doesn't give an answer about the "return value" of the "CustomerComments" view.
The controller handle the event "ShowCustomerComments", create the view, show the view as modal, and when the view is closed, could you tell me how the controller communicate the "result" to the presenter that raised the event "ShowCustomerComments" ? Do we have to raise an other event ?
Thanks for your help.
Aug 14, 2007 at 3:10 PM

gherold wrote:

The implementation of Chris using an event to communicate between the presenter and its controller doesn't give an answer about the "return value" of the "CustomerComments" view.
The controller handle the event "ShowCustomerComments", create the view, show the view as modal, and when the view is closed, could you tell me how the controller communicate the "result" to the presenter that raised the event "ShowCustomerComments" ? Do we have to raise an other event ?
Thanks for your help.


When you are using a WindowWorkspace to show a View in a modal way and you need the View to return results to some sort of controlling WorkItem, the best course of action is to have the Presenter for that View fire an EventBroker event and pass the results as the argument. Obviously that means putting some sort of trigger (button) on the View to cause that to happen. But the benefit is that once the WorkItem receives the event, it can take the data and save it, but it can also use that event to Close the View on the WorkSpace.
Aug 14, 2007 at 3:51 PM
Edited Aug 14, 2007 at 3:57 PM
Hello Chris,
Thanks a lot for answering my post...
If I understand well that means that for a very "basic" navigation problem such as : View1 has a button that shows View2 which allow to select something that is return to View1.
We need 2 events :
- one between Presenter1(pub) and Controller(sub) : to say to the Controller to show View2 as modal form
- one between Presenter2(pub) and Presenter1(sub) : to get the selected value of View2 in View1

Is there a simpler solution ?
Can't the Presenter1 have a reference on its Controller and just call a method like :

public class MyController {
public SelectedValue GetSelected() {
// Create View2 + ShowView2 in a modal windowworkspace
// Set return value of the method if DialogResult is OK
}
}

No event needed for this solution. Could you tell me what do you think about this approach?
Thanks a lot.
Aug 16, 2007 at 8:12 AM
Edited Aug 16, 2007 at 8:12 AM
Hello guys
Got no answer to my previous post...
Could you tell me what's your point of view on having a reference to the controller in the presenter to comunicate from the presenter to the controller (instead of raising an CAB event) ?
Thanks for your help.
Aug 16, 2007 at 3:42 PM

gherold wrote:
Hello Chris,
Thanks a lot for answering my post...
If I understand well that means that for a very "basic" navigation problem such as : View1 has a button that shows View2 which allow to select something that is return to View1.
We need 2 events :
- one between Presenter1(pub) and Controller(sub) : to say to the Controller to show View2 as modal form
- one between Presenter2(pub) and Presenter1(sub) : to get the selected value of View2 in View1
Is there a simpler solution ?


Sure there's a simpler solution. The first thing you have to ask is: Why is presenter1 dependent on a value from presenter2? Shouldn't it be the responsibility of Presenter1 to get that value?

If we're talking about a simple value request from the user, I'd do this:

(1) Have Presenter1 make a request to its view to get the value.
(2) Have the View1 show a dialog to get the value from the user
(3) Have View1 take the results of the dialog and return them to the presenter.

Even simpler: Remove step (1) and have View1 show the dialog when the button on the view is pressed.

The only time I would use the more complex scenario is if View2 needed to be really complex and show a lot of data. For most situations a simple dialog will work, in which case you can delegate that to the View and it can take care of that request.


Can't the Presenter1 have a reference on its Controller and...


Gads no! Now you're coupling your presenter to your controlling workItem. What happens when you want to run that same View in a different context, with a different controlling workItem in charge? What happens when you want to unit test your presenter in isolation? (Now you have to mock the controlling workItem, more work).

That's just bad design.

Here's some basic rules to work with CAB (and these are true of building software in any system)

(1) Separate concerns. You can achieve this by writing more classes (instead of fewer) and smaller methods (instead of larger). A class should have a very focused vision; it should have a very focused responsibility that you can easily describe.

(2) Keep things simple without coupling objects. "Keep things simple" by itself is not enough of a directive, otherwise developers just start referencing everything directly, creating tons of dependencies, and you end up with a highly coupled ball of yarn that can't be unraveled or refactored easily.

If you practice TDD this becomes even more apparent, as isolating objects becomes necessary in order to test them. In order to isolate objects you need to minimize dependencies. That happens by creating small, very focused classes. The more focused a class is the fewer dependencies it will need in order to do its job. That makes it easier to isolate for testing, which makes it easier to code and ensure it is doing its job.

Small classes that are loosely coupled (few dependencies) can create a rich application when you plug them together with things like events and IoC.


Aug 17, 2007 at 8:24 AM
Edited Aug 17, 2007 at 8:27 AM
Thanks Chris for your answer.
Of course, my question was about showing a CAB view 2 from a CAB view1. (View 2 is not a simple "input" view without presenter).
In our approach, for a view that needs to be used by two controllers, we would create a presenter hierachy :
- ViewxPresenter (holding the common behaviour which is not depend of the running controller, without any Controller reference)
- ViewxPresenterForController1 : ViewxPresenter (holding the view behaviour which is specifiq to the controller1, and having a reference to Controller1)
- ViewxPresenterForController2 : ViewxPresenter (holding the view behaviour which is specifiq to the controller2, and having a reference to Controller2)

And when we instanciate the view, we use a syntax such as :
- Controllerx.AddView<View"x", Presenter"y">()

What do you think about this approach ? I've seen a other post about a generic type "Presenter<WorkItem,IView>" that seems to go in this direction as well...

I think that, for you, Presenters should NOT know about any Controller types. And we should be able to put all the Views AND Presenters in a project separated from the project holding the Controller types ( with only the reference Controller Project -> View/Presenter Project ). Is it your point of view ?

Could you tell me exactly what would be your solution in my sample (One CAB view showing and getting a result from an other CAB View showned as Modal).
How many events ? Who publish and subscribe each event ?

I really want to make the good design choice before starting uisng CAB in a quite big application.
Thanks a lot.








Aug 17, 2007 at 3:48 PM

I think that, for you, Presenters should NOT know about any Controller types. And we should be able to put all the Views AND Presenters in a project separated from the project holding the Controller types ( with only the reference Controller Project -> View/Presenter Project ). Is it your point of view ?


Library references are not a major concern, in my opinion. I don't care about being able to put views & presenters in a separate assembly from their controllers. That doesn't gain much.

What I care about is being able to reuse components. To do that requires decoupling classes from each other. Every time you call someObject.SomeMethod() inside a class, you're creating a dependency. Dependencies are necessary, but I like to minimize them where possible (and use interfaces, so concrete versions of dependencies can be swapped).



Of course, my question was about showing a CAB view 2 from a CAB view1. (View 2 is not a simple "input" view without presenter).
In our approach, for a view that needs to be used by two controllers, we would create a presenter hierachy :
- ViewxPresenter (holding the common behaviour which is not depend of the running controller, without any Controller reference)
- ViewxPresenterForController1 : ViewxPresenter (holding the view behaviour which is specifiq to the controller1, and having a reference to Controller1)
- ViewxPresenterForController2 : ViewxPresenter (holding the view behaviour which is specifiq to the controller2, and having a reference to Controller2)

And when we instanciate the view, we use a syntax such as :
- Controllerx.AddView<View"x", Presenter"y">()

What do you think about this approach ? I've seen a other post about a generic type "Presenter<WorkItem,IView>" that seems to go in this direction as well...


I think if you have functionality that is common in two or more presenters then yes, it makes sense to abstract those common features to a base class. That is what object inheritance is for.


Could you tell me exactly what would be your solution in my sample (One CAB view showing and getting a result from an other CAB View showned as Modal).
How many events ? Who publish and subscribe each event ?


Well, as I said above, we probably wouldn't use a second View in a WindowWorkspace. We've moved away from doing that and instead have ViewA create a Dialog. ViewA then gets the DialogResults and returns those to the Presenter. Especially for anything Modal, which is going to block the rest of the application from the user, we delegate the responsibility to the View and have it get the data from the user via a Dialog.

But, if I had to use a WindowWorkspace for some reason, then this is how I'd do it (and this is how we've done it in the past, before we moved to the Dialog approach)

Here's the basic pieces:

Controlling WorkItem (Controller)
ViewA with PresenterA (ViewA)
ViewB with PresenterB (ViewB)

- - - - - - - - - - - - - -

Order of operations:

(1) The Controller creates ViewA and shows in whatever your normal Workspace is.

(2) User clicks a button on ViewA to open a second view in a modal dialog fashion. ViewA calls PresenterA and tells it to ShowViewB.

(3) PresenterA has an EventBrokerPublication to GetDataFromUser/ShowViewB (I leave the naming convention to you). It fires that event.

(4) Controller Subscribes to the GetDataFromUser/ShowViewB event. Controller creates ViewB and shows in a WindowWorkspace with a Modal setting = true.

(5) ViewB gets some data from the user. User clicks a button on ViewB saying they are finished. ViewB wraps up the results and sends to PresenterB.

(6) PresenterB has an EventBrokerPublication for CloseViewB. It has event arguments of type EventArgs<MyObject> or whatever your data is.

(7) Controller Subscribes to the CloseViewB event. It gathers the data from the event arguments and then calls Workspace"WindowWorkspace".Close(ViewB) to close the view.

(8) Controller has an EventBrokerPublication for UpdateViewWithNewData. It has an event argument of type EventArgs<MyObject> or whatever your data is.

(9) PresenterA Subscribes to the UpdateViewWithNewData event. It grabs the results off the event args. It updates ViewA with the data it fetched.

- - - - - - - - - - - - - - - - -

All total it is three events. But the responsibilities are clear: ViewA/PresenterA never knows about the Controller or ViewB. It just fires an event telling someone else (in this case the Controller) to open view B (or get data from user, whatever naming convention you want to use). It isn't dependent on a callback result. Instead, it listens for a second event which passes it data and tells it to update itself.

The Controller's responsibilities are very clear and narrow: It handles the lifecycles of the views it creates. It creates ViewA & ViewB, and eventually closes ViewB. It also routes message data from ViewB to ViewA during the closing event so that operations can happen in a specific order. This is why you should not have PresenterA listen for the Close event. It's not its responsibility, first of all, to Close ViewB. That's the Controller's responsibility. But second, you want to ensure operations happen in a specific order: View closes, then other view updates.

ViewB's responsibilities are very clear and narrow too. It simply fires one event (its presenter does) that sends off the data it has gathered. It knows nothing about the Controller or other View. ViewB could be shown modally, or non modally, or it could be shown by another type of Controller.

Each one of these classes would be super easy to isolate and unit test.












{quote}
Aug 20, 2007 at 9:10 AM
Thanks Chris for your very clear answer.
I think I will have a lot of difficulties to tell my developpers to use 3 events in such a basic scenario...
I became aware that sometimes the "CAB approach" is not always the good way to go...
Thanks a lot for sharing with us your experience.
Aug 20, 2007 at 12:19 PM
Edited Aug 20, 2007 at 12:26 PM

graemefoster wrote:
Hi all,

I've got a requirement to show a view where the user can enter and send email's (pretty much like Outlook's create messages).
Part of the functionality is for a user to be able to select recipients from their address book (not Outlook's but our own aplications).

I'm trying to think of the best way to handle this - firing an event from the Presenter which would be picked up in an address book's controller / presenter feels a bit loose to me as I know I will be getting control right back from the address book once the user has selected.

The ideas I've had so far are

1) The WorkItemController creates both View/Presenters. When the user clicks the "..." button to select email addresses the Presenter raises an event which is captured by the address book Presenter and causes the next view to show modally. When the user has selected email addresses, a new event is raised which is picked up by the original view. This doesn't feel like what the event pattern is trying to achieve to me.

2) I register the 'select email addresses view' as an ICoreAppServices service on the root work item. The WorkItemController only creates the 1st view, and when the '...' button is selected, the Presenter gets the ICoreAppServices and calls ICoreAppServices.ShowAddressBook() which returns an ICollection<EmailAddresses>.

Both approaches seem testable to me, but 2 stands out as the event pattern seems to loose - It relies on something listening to that event when in this case it is imperative that something is listening!

What do you all reckon is a good solution?

Thanks,

Graeme



I have other Ideeas.

1. To show the address book as response of some command ( from menu or toolbar ). This mean that your view will execute an command (ShowAddressBook) rather then rise an event. Root WorkItem will create the view, but not show him. Will wait for sombody to rise ShowAddressBook command to do that.

2. Create Address Book view in Common library, Create an Host view derived from Form, wich will have an smartpartplaceholder for that Address Book view. In your Presenter add both views, Address Book view and then Host view, to his WorkItem, and show Host View (which will automaticaly show the address book). You will have access to Address Book view for reciving the selected addresses.
In this way you decoople what address book should do (showing address and sned the result to some view) from how this address book should be shown (in modal dialog, or in separate window or in some other workspace) maybe you'll need both.. And in this case, all kind of Host view could be from Common library.

Aug 20, 2007 at 3:04 PM
Edited Aug 21, 2007 at 8:59 AM
Hi gherold,


gherold wrote:
Thanks Chris for your very clear answer.
I think I will have a lot of difficulties to tell my developpers to use 3 events in such a basic scenario...
I became aware that sometimes the "CAB approach" is not always the good way to go...
Thanks a lot for sharing with us your experience.


I guess you can explain the advantage of even any basic three-event scenario presenting any other CAB's feature. Let's say you can show that using events in any scenario you can keep the entire application highly and easily maintainable.
- It's really easy to add a new module to the already compiled and deployed applications that has a controller which reacts on the same events and let's say display confirmation boxes before opening or closing a view. You could enable/disable this feature just commenting/uncommenting the new modile in xml profile catalog.
- You can add a new module which adds a main menu to the shell with command handlers which raises events to show views from the other modules.
- etc

I suppose it could be easier than you thought to enumerate real time-saving advantages of this approach to the team.

The only problem I have run into in such a situation was to estimate correctly the time it could take to understand and start developing using SCSF/CAB.

-
Thanks, Leonid
Aug 21, 2007 at 4:24 AM

gherold wrote:
Thanks Chris for your very clear answer.
I think I will have a lot of difficulties to tell my developpers to use 3 events in such a basic scenario...
I became aware that sometimes the "CAB approach" is not always the good way to go...
Thanks a lot for sharing with us your experience.


I think it is worth noting that event driven applications are not something unique to CAB. My solution was not necessarily a "CAB approach"; it was an approach I'd take even if I were not using CAB.

Events are a common construct in most major application programming languages; they are a centerpiece in .NET and every developer should feel comfortable using them.

If you think you'd have problems describing this sort of 3-event logic to your developers then you may want to rethink the developers you've hired. It is also entirely possible you're underestimating their skills and abilities and they are more capable than you imagine.

I think too often people make the mistake of thinking events are complicated when they are really not. If you think about it, events are not very different from method calls. They both achieve the same thing: one object is able to call another object and ask it to do some work by invoking a method and passing some parameters. A method call is more direct (and easier to see the relationship between the two objects in code), but also increases coupling by a significant margin, which ultimately makes the code harder to test, maintain, and change. Events have the advantage of being loosely coupled. They also provide an additional feature: they can be received by more than one listener (multicast).

The only complexity in this scenario is the program flow, and whether you use events or not the program flow is still the same. A button needs to cause a view to open (1); that view needs to send its results to another object (2); that object needs to update a view with the results (3). You will not be able to do all of that in one method call. If you did, I would not want to see that code, maintain it, or attempt to test it or modify it.


I became aware that sometimes the "CAB approach" is not always the good way to go...


The best choice is always to do things the right way. In order for that to happen you must be able to trust your developers, and you must invest in writing the best code you can possibly write. Anything less than that will be cheating your customers and ultimately yourself.