Search code examples
asp.netmvp

How should the View pull on the Presenter in the MVP pattern


I have a ASP.NET Web Forms application and I'm using some dynamic controls in the view which depend on stuff that the presenter exposes. Is it okay for the view in this case to pull on the presenter for that data? Is there anything I should be extra careful about when considering testability and a loosely coupled design.

The page in this case has it's own page-life cycle and the presenter doesn't know about this. However, the page-life cycle dictates that somethings must occur at specific moments in the page-life cycle. This smells like trouble... Any known pit falls?

EDIT

When my concrete view hits the Init event it will pull on the presenter for a collection that will result in a bunch of ASP.NET server controls to be created. I'm wondering if this is a particular bad thing to do... The presenter doesn't know how to respond to the page-life cycle init event but it has to if the view is to be populated with dynamic controls.


Solution

  • This is a vexing question - when you want dynamic controls in ASP.NET web forms you've got to choose the lesser of two evils. Ultimately, it comes down to which compromise you want to make: presenter concerns in the view or view concerns in the presenter.

    When I work in web forms I usually prefer the latter - I accept that the presenter is tied to the ASP.NET page lifecycle and create an InitializeView method to do whatever's necessary to create dynamic controls:

    // Presenter
    // This could also be parameterless if you prefer that idiom but 
    // then the view needs a SelectedState property that serves up values
    // straight from the Form collection, and it won't be obvious why.
    public void InitializeView(string selectedState) { 
        if (selectedState != null) {
            view.Counties = dataLayer.GetCounties(selectedState);
        }
    }
    
    // View
    protected void Page_Init(object sender, EventArgs args) {
        presenter.InitializeView(Request.Form["StateList"]);
        // ... build counties drop-down ...
    }
    

    Of course, this ties the presenter's semantics to the ASP.NET lifecycle and obscures what's going on in that method. You can mitigate that by giving InitializeView a more descriptive name, like ProcessSelectedState, but unless the method name references the page lifecycle, it's never going to be obvious why you're not just getting counties with the rest of the model (in presenter.LoadModel or whatever you might call it).

    I can see how the alternative is attractive:

    protected void Page_Init(object sender, EventArgs args) {
        if (Request.Form["StateList"] != null) {
            List<string> counties = presenter.GetCounties(Request.Form["StateList"]);
            // ... build counties drop-down ...
        }
    }
    

    The semantics of the Presenter are perfectly clear - it's easy to understand what GetCounties does and it doesn't have anything to do with the page lifecycle. But you have testable stuff in your view, which is a bummer, and that's usually more important to me than keeping my presenters ignorant of their view engine.

    Another alternative is to just load your entire model during page initialization. Your server control values won't be available, so you'd have to get any of those from Request.Form. That's not idiomatic ASP.NET classic though - it might be unduly confusing since most web forms developers are accustomed to getting values directly from web controls instead of directly from the POST data.