9

This question is regarding MVP triads. Lets say I have two triads and first one has View1, Mode1 and Presenter1. Second triad has View2, Model2 and Presenter2. Now what I trying to do is, I just want to display the View2 when I click a button in View1.

You'd show the View1 calling it's presenter's show method.

View1Presenter presenter1 = new View1Presenter (view1, Model1);
presenter1.show();

Now When I click btnShowView2 button on the View1, it should display the View2.

So how to call presenter2.show() from View1?

CAD
  • 345
  • 2
  • 5
  • 16
  • This question appears to be off-topic because it is about implementation and has already been posted at StackOverflow. – maple_shaft Jun 16 '14 at 13:29
  • 2
    @maple_shaft: this question is IMHO clearly on-topic, since it is question about responsibilities in the code when follwing the MVP paradigm. It is not even language specific, C#/Winforms is here just an example, in Java, C++ or many other OO languages the question & answer would almost look like the same. Of course, it is a crosspost (and should be closed on SO!) – Doc Brown Jun 16 '14 at 14:23
  • @DocBrown Thanks for bringing that to my attention. I didn't read the question carefully enough. I reopened. – maple_shaft Jun 16 '14 at 16:16

1 Answers1

6

Displaying a second view on btnShowView2.Clicked is some kind of business logic, so the right place for implementing this is in the event handler of presenter1 which deals with that event (I assume that presenter1 is registered to all relevant button-click events of View1). Lets call it HandleBtnView2Click, so the initial code inside that handler might look like this:

  void HandleBtnView2Clicked(object sender, EventArgs e))
  {
       IView2 view2 = new View2();
       Model2 model2 = model1.GetRefToModel2(); // or whatever is needed to get model2
       View2Presenter presenter2 =new View2Presenter(view2,model2);
       presenter2.show();
  }

But actually, this is not a good solution, since it would imply the presenter2 layer must be linked against the UI library (in your case WinForms), which in worst case prevents automatic testing.

The better solution is to delegate the creation of View2 to a third instance, which can be easily mocked out. In general, its possible to use an abstract factory for this. In the specific case, it is also possible to use View1 for the creation of View2. Add a method to your IView1 interface:

 interface IView1
 {
    // ...
      IView2 CreateView2();
 }

and use it in the code above:

  void HandleBtnView2Clicked(object sender, EventArgs e))
  {
       IView2 view2 = view1.CreateView2(); // view1 is of type IView1 and a member of presenter1
       Model2 model2 = model1.GetRefToModel2(); // or whatever is needed to get model2
       View2Presenter presenter2 =new View2Presenter(view2,model2);
       presenter2.show();
  }

Now when you want to automatically test your presenter1, provide it with a mock implementation of view1 which implements the CreateView2 method in a manner returning a mock of view2. In the View1 class, you implement CreateView2() to return the new View2(), so the responsibility for creating views stays at the the view layer.

You can go a step further if you want and make presenter1 testable in isolation from presenter2. If you think that is necessary, create an interface IPresenter2 and put the whole creation code above into an abstract "presenter factory":

   void HandleBtnView2Clicked(object sender, EventArgs e))
   {
        IPresenter presenter2 = presenterFactory.CreatePresenter2(model1);
        presenter2.show();
   }

The presenterFactory has to be injected into your presenter1 at construction time and can be replaced by a mock in your test suite. If that's necessary, you have to decide for yourself. It will add an additional layer of complexity to your code for the benefit of better testability.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565