Freigeben über


WCSF Application Architecture 3: Model View Presenter

This article is part of a series;

· WCSF Application Architecture 1: Introduction

· WCSF Application Architecture 2: Application Controller

Model View Presenter

The MVP pattern is effectively an evolution of Model View Controller, and is mainly geared at improving the structure and testability of User Interface code. There is a good article here that discusses UI patterns, and a previous post of mine that contrasts the MVP pattern with a pre-release version of the ASP.NET MVC Framework. The Web Client Software Factory (WCSF) uses the MVP pattern, and relies on Dependency Injection to wire up the class dependencies.

The following diagram should help you visualise how MVP is put together;

MVP

 

Note that each Presenter has exactly one View, and each View has exactly one Presenter. There is one Model for the whole application (ahem, see later posts on modularity J).

Let’s consider each item in turn.

 

Model

The model is often misunderstood. It sounds like it should just be data, but then where does my business logic sit? Well, I see the model as representing the whole of my business logic – entities, workflows, and components. It is the window into the real non-UI part of my application. Sticking to our standard architecture defined in the introduction post, the model should only expose business logic components. It should not surface data access components, web service proxies, or other private classes that are further down the architecture stack.

Note that in much of the WCSF documentation the “Model” in MVP is silent – mainly because you could argue it isn’t really a key part to the structure of the UI code, as it is business logic. Hence you might see MVP referred to as “the View-Presenter pattern”. It’s the same thing...

We will discuss the model, how to structure it, and how to implement it in later posts, but for now just think of it as a bunch of plain old .NET objects that “do business logic”.

 

View

A view is used to display data to the user, and therefore in the WCSF is an ASP.NET “aspx” page and its code behind. In the WCSF, this aspx page must implement an interface (e.g. “IHomePage” would define the Home Page view, “HomePage.aspx” would implement it, and it would have a matching presenter named “HomePagePresenter”). The code that exists in this class should be very simple – and should be focussed on two simple tasks;

1. Taking instructions from the Presenter and converting them into ASP.NET calls.

2. Taking events from the user (such as button clicks) and passing them on to the Presenter to field.

Effectively, the view is responsible for translating between the Presenter and ASP.NET concepts.

Note that on the pattern diagram above I really wish I could remove the arrow from the view to the model. This is because when I use MVP I enforce a rule that the View should under no circumstances call directly to the business logic. The only reason the arrow is there is because the view may have read-only access to some business entities for rendering purposes only. This is heading towards the “supervising controller” variant of the pattern described in the WCSF documentation here, which I find to be the only practical way to implement data-binding.

 

Presenter

A presenter instructs a view what should be displayed and when, and responds to events raised by the view. It should be unaware of ASP.NET concepts. It should also be the class that interacts with the business logic, potentially passing on single or multiple business entities to the view for rendering. In other words, it is the go-between for business logic and rendered UI.

In the WCSF the presenter has a reference to the view as defined by an interface, hence the two-stage and dotted link in the diagram above.

 

An example

So after all this theoretical talk, let’s see a really simple example of a Model-View-Presenter trio and how they work. Have a look at the following (simplified for clarity) code.

interface IMyView

{

  void DisplayText(string name);

}

class MyView : IMyView

{

    public MyViewPresenter Presenter { get; set };

    public void DisplayText(string name)

    {

        TextBox1.Text = name;

    }

    public void ExitButton_Clicked(object sender, EventArgs e)

    {

        Presenter.OnExit(Page.User.Identity.Name);

    }

}

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit(string username)

    {

        BusinessLogic.ExitUser(username);

        View.DisplayText("You clicked Exit!");

    }

}

What we have here is the following;

1. An interface that defines the operations that our View exposes (just “DisplayText” in this case).

2. An implementation of the view (aspx mark-up omitted in this example). This receives instructions such as “DisplayText” and translates them into ASP.NET concepts – such as setting the Text property on a TextBox.

3. A presenter, which instructs the View what to do during initialisation, and responds to View events (such as Exit). Note that the Exit method calls into business logic and then updates the view.

I hope that is a nice clear simple example of the interaction between the classes.

 

How do I get my data?

This example contains a really interesting yet subtle point, and that is how to pass data from a view “up” to the presenter. In my code, I make the following call;

Presenter.OnExit(Page.User.Identity.Name);

What this means is that my view is responsible for collecting the relevant data and passing it on to the presenter at the appropriate time. Now this isn’t necessarily wrong, and in many cases is the easiest way to write code – but actually I think it is sub-optimal.

The issue is that we already have an interface defining what our view provides, and I feel that this should really be the contract between presenter and view for as much information as possible. Therefore, I would prefer the following implementation;

interface IMyView

{

    void DisplayText(string name);

    string CurrentUsername { get;}

}

class MyView : IMyView

{

    public MyViewPresenter Presenter { get; set };

    public void DisplayText(string name)

    {

        TextBox1.Text = name;

    }

    public void ExitButton_Clicked(object sender, EventArgs e)

    {

        Presenter.OnExit();

    }

    public string CurrentUsername

    {

        get

        {

            return Page.User.Identity.Name;

        }

    }

}

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit()

    {

        BusinessLogic.ExitUser(View.CurrentUsername);

        View.DisplayText("You clicked Exit!");

    }

}

(note: changes are in italic)

What I’ve done here instead is expose the user’s login name as a property on the view, which is defined in the interface that serves as the “contract” between the presenter and the view. I then just raise the “OnExit” event from the view to the presenter, and it is the presenter’s responsibility to extract the information it needs from the view.

This example uses the username, but I would also use it for passing the contents of a textbox edited by the user through to the presenter – expose a property on the view that the presenter extracts the required text from when needed.

I personally think this is more elegant, plus I find it actually helps when writing unit tests. What do you think?

 

MVP + Application Controller

The final point to make is where the interaction with an Application Controller fits in. Hopefully the diagram below helps;

MVP plus AC

I believe that, if you go with my preferred approach to the pattern described in a previous post, the presenter should be the only point of interaction with the Application Controller. So in my simple example above, you might have code that looks like this;

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit()

    {

        BusinessLogic.ExitUser(View.CurrentUsername);

        ApplicationController.CompleteExit();

    }

}

The CompleteExit method would more than likely redirect the user to the home page.

 

Summary

MVP is subject to less confusion once you have ironed out what an Application Controller does, but there are some key points to remember;

1. There should be no logic other than simple data binding or property access in a View.

2. There should be no ASP.NET concepts in a Presenter or the Model.

3. The View must not directly call Model (business logic) methods, but may have a reference to business entities to simplify rendering and data binding.

4. The Presenter should orchestrate the relationship between the View and the Model (business logic).

Following these guidelines should help enable you to unit test your presenters effectively, and should make you smile when you have to make a change to the code... meaning you know exactly where and how to make that change.

Keep listening to hear about modularity in the WCSF, and as always – chip in if you agree, disagree, or don’t think I’ve explained something sufficiently...

Comments

  • Anonymous
    June 16, 2008
    This article is part of a series; · WCSF Application Architecture 1: Introduction · WCSF Application

  • Anonymous
    June 18, 2008
    This article is part of a series; · WCSF Application Architecture 1: Introduction · WCSF Application

  • Anonymous
    July 03, 2008
    This article is part of a series; · WCSF Application Architecture 1: Introduction · WCSF Application

  • Anonymous
    July 16, 2008
    Until now, I had problems in understanding the m-v-p Pattern, especially in combination with the controllers. The roles of each element (model, view, presenter and controller) and the communication between the elements are clear now. One question remains for me: Where do you manage flow and states? I came up with the following scenarios:

  1. use case flow and states: The place to manage use case states is the controller.  E.g. the Create Order Use Case has several steps: Search Product, Select Product, Enter Customer Data, Submit, Approve, ShowConfirmation... The controller nows, in which state the user is.
  2. internal view flow and state: A view can provide different actions. E.g. a list can be sorted, filtered,...This does not result in a different use case state. In this case, the presenter is responsible for handling the state and the flow of action. E.g. the presenter holds the Customer-Array, sorts it and forces the view to display the sorted list.
  3. Cross use case flow and states: What might be interesting is: How do you deal with use cases that call sub use cases and then continue? Or use cases that call sub use cases as modal dialog. E.g. the "Create Order Use Case" calls the "Select Customer" use case in a modal dialog. There is the  Page Flow Application Blog, which I haven't digged into yet. As I understand it, it is used for scenario 1 (handling use case flow). But what about scenario 3? I am really interested in any kinds of thoughts and solutions... Rainer
  • Anonymous
    July 16, 2008
    The comment has been removed
  • Anonymous
    July 16, 2008
    Hi Rainer and Simon. The Page Flow Aplication Block was not built to handle sub-flows. It can only handle 1 flow at a time and it will only allow one running instance of each page flow type. There are a number of things you can do to change this.
  1. The main obstacle to the use of multiple page flows is the correlation token being stored in a cookie. If you change this behavior to store it in an hidden field (either by forcing the use of a base page or changing the Page Flow HTTP Module to force one in the page). With the new ability to run simultaneous page flows, you can have a service where you register the URL these client side services and keep page flows decoupled.
  2. Another way is using virtual path providers or a router (like the MVC router) to virtualize all navigations in the page flows. This allows different page flows to share pages and you can copy and paste this sub flows into the page flows. Because now you have forked the navigation you can no longer have a single current page. You’ll need to create the concept of paths in the same page flow. Like Simon, I haven’t used in the Page Flow Application Block in live deployments myself. But I have built a similar one adapted to y company’s needs using the second approach.
  • Anonymous
    July 18, 2008
    This article is part of a series; · WCSF Application Architecture 1: Introduction · WCSF Application

  • Anonymous
    July 31, 2008
    Preguntas como por que se colocan las propiedades en la interfase de la vista, que debe de ir en el Application

  • Anonymous
    August 23, 2008
    Hello, I really liked this article but I have a question to ask. Where I work, we modified this WCSF MVP approach a bit. Instead of the View calling Presenter.OnExit(), the view would fire an OnExit event that is subsequently handled by the Presenter without the View actually calling a method implemented in the presenter. I don't like the approach that we are taking. Do you feel that it has any justtified merits or does it only add more complications?

  • Anonymous
    August 23, 2008
    I think it really depends on how many views are calling Presenter.OnExit(). If Presenter.OnExit() is always (or most of the times) called, I think that was a bad idea.

  • Anonymous
    August 24, 2008
    @ sabb0ur; Interesting, and I actually quite like the event driven approach - it fits very closely with how I like to work (in that the View just passes on events to the Presenter). I haven't used real events in the past because I thought there might be issues with subscribing to the events at the right time. When do you add event handlers to the view? A few possibilities spring to mind but I'd be interested to hear how you've gotten on with this. I guess my concern is that you won't have subscribed to an event in time before the View fires it. If you've got this working, though, I'd personally quite like this model; especially if you're avoiding passing state in the events and rely on the Presenter inspecting the View's state as I describe above. Hope that helps! Simon

  • Anonymous
    August 24, 2008
    I've been also working on a similar MVP pattern for ASP.NET. The one thing that I haven't yet seen discussed much when reading about the WCSF is that it appears like the pattern suggests 1 view per aspx page. I tend to think of using views more like the CAB, where there are multiple (at times) per screen, and that "view presenters" can communicate to other view presenters using an event broker mechanism (but the view communicates with the presenter through standard .net delegate based events). I haven't yet used this in a real world project, but it seems to work well and seems to be a nice pattern.  What are other's thoughts about using multiple views per page in WCSF. Is it designed for this?

  • Anonymous
    August 25, 2008
    The comment has been removed

  • Anonymous
    August 27, 2008
    Simon, Just a clarification on MVP + Application Controller, Using WCSF(VS2008):

  • Web Client Factory > Add Page (with Presenter), this will create dafault.aspx, dafault.aspx.cs & IdefaultView.cs (for it's View) & defaultPresenter.cs (for it's Presenter), but where is the Application Controller? Does it mean I need to manually create a defaultApplicationController.cs (for it's Application Controller)? If yes, where should i put it? is it the same location as the defaultPresenter.cs? Are all Presenter should always have a corresponding Application Controller if where are talking of MVP + Application Controller? Thanks, Chris
  • Anonymous
    August 27, 2008
    Chris; very good question, I don't think I've mentioned that anywhere!! Application Controllers are created by the factory when you create a new module - there is by default a single controller per module named MyModuleController.cs in the root of the C# project. In general you should have much fewer controllers than views/presenters because they should be managing the flow between views when you have a "page flow" - I'd check out the "what is page flow" section in my application controller post (http://blogs.msdn.com/simonince/archive/2008/06/06/wcsf-application-architecture-2-application-controller.aspx) for more info, as this discusses when you should or should not bother with using a controller in my opinion. One thing I do also discuss in my "defining modules" post (http://blogs.msdn.com/simonince/archive/2008/06/19/wcsf-application-architecture-5-defining-modules.aspx) is adding new controllers when appropriate - check out pain point number 7. So in other words, the factory adds a single controller for you per module, there should not be a controller per presenter, and if required you can add your own controllers very easily! Hope that helps! Simon

  • Anonymous
    May 28, 2009
    The comment has been removed

  • Anonymous
    May 28, 2009
    @ Hongliang, good questions. Above I did mean the WCSF MVP versions of user controls, but there is more to it than that. I think the confusion comes down to there being two main types of controls... Those that contain logic, and are effectively encapsulated system behaviour themselves, and those that are just display controls. For example, you might have a User Control that handles displaying a customer record, that formats the output, allows you to edit their address, click a button to view their orders, and flag them as missing payments. This would likely have an MVP pattern behind it, and I think it can be described as a "view". You might also have a control that is used purely for rendering - it might be business specific and display a customer address, or be business agnostic and display a numeric-only field that has "up" and "down" buttons next to it. This wouldn't have an MVP pattern, and isn't a "view" itself. Both of these are valid ways to reuse logic and controls, but only one is a sensible way to compose "views" into multi-view screens (e.g. master-detail screens)... which is the use case I was referring to. Does that help? Simon

  • Anonymous
    May 29, 2009
    Hi Simon, Thank you very much for the prompt response. It is most helpful. The two main types of User Controls you described are exactly what I have been struggling with. In particular, I found it not always easy to recognize which type my new User Control belongs to. For example, I have a panel of search/filter controls including both TextBoxes and DropDownlists as well as a submit button, which I would like to refactor into a separate User Control. This User Control itself does not feel like a complete "View" for me, since its fuction is very simple -- collect and send search criteria, and the search result will be shown in another GridView on the page outside the User Control. On the other hand, I do need to contact backend service to both pre-populate the dropdowns and to submit the search request eventually, both of which can be counted as "logic". I guess what I am troubled with is the need to decide case-by-case and the architectural impact of any wrong judgement. Furthermore, if I "pollute" an existing display-only user control with business logic, I will need to manually change the base class of the User Control, and create interface and presenter for it, which makes me want to rather create every user control with MVP "just in case". Also, I wonder what is the recommended way for the "host" page's Presenter to co-ordinate calls between the (MVP enabled) User Controls. Since the page presenter can only access the page via its view interface, it looks the User Controls need to be exposed as part of the page view interface. So the question is in which fashion the User Control should be exposed. Should the IUserControlView be added as a (read-only) property of the IPageView, or should the IPageView add some wrapper memebers around the memebers of each of the IUserControlView's? Or, is it totally out-of-question for the page presenter to directly invoke methods on user control presenters? I apologize if above questions are too implementation-specific or too detailed to be relevant to the main topic here. As always, any of your comments would be greatly appreciated. Thanks, Hongliang

  • Anonymous
    June 16, 2009
    The comment has been removed

  • Anonymous
    December 09, 2009
    Thanks for the nice blog/ article. Really helped me understand my appln which also follow MVP arch.

  • Anonymous
    May 13, 2010
    @ Simon and Hongliang I know this post is quite old, but I am in the exact same situation that Hongliang has described. I think the biggest difference is that I am developing WinForms applications, but the pattern applies the same. I have searched for a few days now, reading many posts/blogs, but still have yet to read (or better yet, see!) that one piece of information that allows it all to "click" for me. I too love to use UserControls as much as possible, but as soon as I add a 2nd UserControl to my form it seems that my brain just shuts down on what to do next. I have recently written an application that contained 5 UserControls (each using the MVP Pattern). One UserControl was for displaying a list of objects held within a collection. One UserControl displayed the details of the selected object from the first UserControl. Another UserControl had actions that could be performed on the selected object from the first UserControl and so on. This was not for work but for my desire to learn the pattern. So I read a few post, watched a video or two, and then jumped into coding. I quickly found myself with 5 UserControls on the same WinFrom, without any clue as how these UserControls would interact with each other. What I ended up doing in the end was exposing the UserControl Interfaces with the main form's Interface. public interface IMainFormView {  IUserControl1 {get;}  IUserControl2 {get;}  ... } In the main form's presenter I subscribed to the events of the UserControls and reacted appropriately. However, this was not a very elegant (or simple) task for me. I even made it worse by passing in the collection to each of the UserControl's presenters so that they could hook up to the events when the collection changed! Argh! I have since realized that maybe I should adopt the App Controller pattern to my application. Immediately I ran into the same problem, except now, I don't know how to get the controls to react to the App Controller lol. That's when I decided to hit the web again. This post is probably the best one that I've come across that someone else has explained my exact situation. It's nice to know I'm not alone, even if it means someone else is suffering lol! I would love any response/advice that anyone could give me on this subject. I would also like to end with my appreciation to those people who make an active effort to create posts like these. Hopefully one day, I can return to favor.

  • Anonymous
    May 13, 2010
    @ JRose22; Have you seen "Prism" (or Composite WPF as it's known)? It is focused on WPF rather than winforms but they discuss UI composition in it quite well; http://compositewpf.codeplex.com/ Fundamentally this question is very hard to answer in Web Forms, but in WPF and WinForms it gets a bit easier. Also in MVC there are more options (just see the Web App Guidance from p&p that uses similar principles to Prism; http://webclientguidance.codeplex.com/). Basically it sounds like you're after "Top Down Composition" (or "View Discovery", so go check that out in their docs and it should help! I've heard that some people have ported Prism to WinForms but even without that the concepts should be useful. The Prism UI composition docs are here; http://msdn.microsoft.com/en-us/library/ff648265.aspx Hope that helps. Simon