When not IsPostBack, the Presenter then sets the <CODE>CurrentTime</CODE> of the view
via its interface. (Sequence diagram purists may raise the point that the
diagram implies two messages are being sent - one from CurrentTimePresenter to
ICurrentTimeView and then one from ICurrentTimeView to CurrentTimeView.ascx -
when in fact only one is being sent from CurrentTimePresenter to
CurrentTimeView.ascx, polymorphically. The interface "middleman" is included to
emphasize that the Presenter does not depend on the concrete View directly.)
D) Presenter InitView after IsPostBack
In the preceding steps, the user made the HTTP request, the Presenter set the
current time on the View, and the HTTP response was delivered to the user. Now,
the user clicks the "Add Days" button which causes a post-back. Everything
occurs as before until <CODE>InitView</CODE> is called on the Presenter. At this
point, the Presenter tests for IsPostBack and does <I>not</I> set the
<CODE>CurrentTime</CODE> on the View.
E) Button Click Handled by User Control
After the Page_Load of the ASPX page has occurred, the OnClick event is then
raised to the user control. The View should not handle the event itself; it
should immediately pass the event on to the Presenter for action. By looking at
the <A href="#EventPasser">code-behind of the user control</A>, you can see that
it makes sure it has been given a valid presenter (more "Design by Contract")
and then hands the command off to the Presenter. then verifies that the page was
valid and sets the time or error message, accordingly.
The above has been an exhaustive analysis of a complete MVP cycle with event
handling. Once you get the hang of MVP, it takes very little time to get all the
pieces in place. Remember to always begin with a unit test and let the unit
tests drive the development. The unit tests not only help ensure that the MVP
pieces are working correctly, they also serve as the point for defining the
communications protocol among the pieces. (A Visual Studio code snippet for an
MVP unit test can be found in We'll now
take a look at look at handling page redirection.
III. Page Redirects with MVP & PageMethods
In developing enterprise application, application flow is always a concern.
Who's going to take care of page redirects? Should action redirects be stored in
a configurable XML file? Should a third party tool such as <A
href="http://mavnet.sourceforge.net/" target=_blank>Maverick.NET</A> or <A
href="http://www.springframework.net/" target=_blank>Spring.NET</A> handle page
flow? Personally, I like to keep the page redirects as close to the action as
possible. In other words, I feel that storing action/redirects in an external
XML file leads to further indirection that can be tedious to understand and
maintain. (As if we don't have enough to worry about already!) On the other
hand, hard-coded redirects in the ASPX code-behind are fragile, tedious to parse
and not strongly typed. To solve this problem, PageMethods, free for download at
<A href="http://metasapiens.com/PageMethods"
target=_blank>http://metasapiens.com/PageMethods</A>, allows you to have
strongly typed redirects. So instead of writing <CODE>Response.Redirect("../Project/ShowProjectSummary?projectId=" +
projectId.ToString() + "&userId=" +
userId.ToString())</CODE>, PageMethods provides a strongly typed redirect that
would look more like
<CODE>Response.Redirect(MyPageMethods.ShowProjectSummary.ShowSummaryFor(projectId,
userId))</CODE>. The redirect is strongly typed and, therefore, checked at
compile time. </P>
<P>An MVP related question concerning page redirects remains: who should be
responsible for making a redirect and how should the redirect be initiated? (I
believe there are a number of valid answers to this question but will propose a
solution that I've found to be rather successful.) Add one event to the
Presenter for each outcome that is possible. For example, assume a website is
made up of two pages. The first page lists a number of projects; the second
page, reached by clicking "Edit" next to one of the project names, allows the
user to update the project's name. After updating the project name, the user
should be redirected to the project listing page again. To implement this, the
Presenter should raise an event showing that the project name was successfully
changed and then the View Initializer, the ASPX page, should execute the
appropriate redirect. (Note that the following is illustrative and not
associated with the "current time" example discussed thus far.)
<H4>Presenter:</H4><PRE>...
public event EventHandler ProjectUpdated;
public void UpdateProjectNameWith(string newName) {
...
if (everythingWentSuccessfully) {
ProjectUpdated(this, null);
}
else {
view.Message = "That name already exists. Please provide a new one!";
}
}
...
ASPX Code-Behind...
protected void Page_Load(object sender, EventArgs e) {
EditProjectPresenter presenter = new EditProjectPresenter(editProjectView);
presenter.ProjectUpdated += new EventHandler(HandleProjectUpdated);
presenter.InitView();
}
private void HandleProjectUpdated(object sender, EventArgs e) {
Response.Redirect(MyPageMethods.ShowProjectSummary.Show(projectId, userId));
}
...
</PRE>Taking this approach keeps page redirection out of the Presenter and out
of the View. As a rule of thumb, the Presenter should never require a reference
to <CODE>System.Web</CODE>. Furthermore, disassociating redirects from the View
- the user control - allows the View to be used again by other View Initializers
- other ASPX pages - while leaving application flow up to each individual View
Initializer. This is the greatest benefit of using an event based model of
redirection with User Control-as-View MVP.
IV. Presentation Security with MVP
Often times, a column, button, table or whatever should be shown/hidden based
on the permissions of the user viewing the website. Likewise, an item may be
hidden when a View is included in one View Initializer vs. being included in
different View Initializer. The security should be determined by the Presenter
but the View should handle how that decision should be implemented. Picking up
again with the "current time" example, assume that the client only wants the
"Add Days" section to be available for users on even days; e.g. 2, 4, 6, etc.
(The client likes to keep the users guessing!) The View could encapsulate this
area within a panel, as follows:
...
<asp:Panel id="pnlAddDays" runat="server" visible="false">
<asp:TextBox id="txtNumberOfDays" runat="server" />
<asp:RequiredFieldValidator ControlToValidate="txtNumberOfDays" runat="server"
ErrorMessage="Number of days is required" ValidationGroup="AddDays" />
<asp:CompareValidator ControlToValidate="txtNumberOfDays" runat="server"
Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"
ErrorMessage="Number of days must be numeric" /><br />
<br />
<asp:Button id="btnAddDays" Text="Add Days" runat="server"
OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />
</asp:Panel>
...
Note that the panel's visibility is pessimistically set to <CODE>false</CODE>. Although it would not make much
difference in this case, it is better to be pessimistic about showing secure
elements than the other way around. The code-behind of the View would then
expose a setter to show/hide the panel: ...
public bool EnableAddDaysCapabilities {
set { pnlAddDays.Visible = value; }
}
...
Note that the View does not expose the panel directly. This is intentionally
done for two reasons: 1) exposing the panel directly would require that the
Presenter have a reference to <CODE>System.Web</CODE> - something we want to
avoid, and 2) exposing the panel ties the Presenter to an "implementation
detail" of the View. The more a Presenter is tied to how a View is implemented,
the less likely it will be reusable with other Views. As with other OOP
scenarios, the pros and cons of exposing implementation details of the View need
to be weighed against looser coupling to the Presenter.
Finally, during InitView, the Presenter checks if the user should be allowed
to use the add-days functionality and sets the permission on the View,
accordingly:
public void InitView() {
view.EnableAddDaysCapabilities = (DateTime.Now.Day % 2 == 0);
}
...
This simple example can be extended to a varied number of scenarios
including security checks. Note that this is not a replacement for built-in .NET
security, but it serves to augment it for finer control.
V. Application Architecture with MVP
Finally! How does all of this fit together in a data-driven, enterprise
application? "Enterprise application," in this instance, is an application that
has logically separated tiers including presentation, domain and data-access
layers. The following graph shows an overview of a fully architected solution
with discussion following.

each raised box represents a distinct specialization of the application. Each
gray box then represents a separate, physical assembly; e.g. MyProject.Web.dll,
MyProject.Presenters.dll, MyProject.Core.dll, etc. The arrows represent
dependencies. For example, the <I>.Web</I> assembly depends on the
<I>.Presenters</I> and <I>.Core</I> assemblies. The assemblies avoid
bi-directional dependency using the techniques Dependency Inversion and
Dependency Injection. My preferred means of Dependency Injection ("DI" in the
above graph) to the View Initializers is via the <A
href="http://www.castleproject.org/index.php/Windsor_Container"
target=_blank>Castle Windsor</A> project. The data layer then uses the ORM
framework, <A href="http://www.hibernate.org/343.html"
target=_blank>NHibernate</A>, for communicating with the database. </P>
<P>For a primer on Dependency Injection, read the CodeProject article entitled
"Dependency Injection for Loose Coupling" at <A
href="http://www.codeproject.com/csharp/DependencyInjection.asp"
target=_blank>http://www.codeproject.com/csharp/DependencyInjection.asp</A>.
Additionally, for a complete overview of this architecture, sans the
<I>.Presenters</I> layer and Castle Windsor integration, read the CodeProject
article entitled "NHibernate Best Practices with ASP.NET" at <A
href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp">http://www.codeproject.com/aspnet/NHibernateBestPractices.asp</A>.
This article also describes how to setup and run the sample application. (Yes,
these are both <I>shameless</I> plugs for other articles I have written - but
both required reading to fully appreciate the sample solution.) Please feel free
to raise any questions concerning the architecture.
In Summary
At first glance, implementing MVP looks like a lot of extra work. In fact, it
will slow development a bit during the <I>initial</I> stages of
development. But after using it in all stages of enterprise application
development, the long-term benefits of using the approach far outweigh the
initial feelings of discomfort with the pattern. MVP will greatly extend your
ability to unit test and keep code more maintainable throughout the lifetime of
the project - especially during the maintenance phase. When it comes right down
to it, I'm not suggesting that you use MVP on all your enterprise ASP.NET
projects, just the projects that you want to work! ;) In all seriousness,
MVP is not appropriate in all situations. An application's architecture
should fit the task at hand and complexity should not be added unless
warrented. Obviously, MVP and User-Control-as-View MVP are
just two architectural options among many. But, if used
appropriately, MVP allows you to be confident in your presentation logic by
making most of the code that would have been in a code-behind, testable and
maintainable.
Appendix A: Additional References
Martin Fowler's soon-to-be-published overview of MVP:
The humble dialog box:
.
<LI>MVC vs. MVP: <A
href="http://www.darronschall.com/weblog/archives/000113.cfm"
target=_blank>http://www.darronschall.com/weblog/archives/000113.cfm</A>.
<LI>A thank you goes out to Jeffrey Palermo for inspiring the above
architectural diagram at <A
href="http://codebetter.com/blogs/jeffrey.palermo/archive/2005/04/02/128598.aspx"
target=_blank>http://codebetter.com/blogs/jeffrey.palermo/archive/2005/04/02/128598.aspx</A>. </LI></UL>
<P></P>
<H2><A name=AppendixB>Appendix B: MVP Unit Test Code-Snippet for Visual Studio
2005
With a default VS 2005 install location, copy the following contents to "MVP
Test Init.snippet" under "C:\Program Files\Microsoft Visual Studio
8\VC#\Snippets\1033\Visual C#." <PRE><?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>MVP Test Init</Title>
<Shortcut>mvpTestInit</Shortcut>
<Description>Code snippet for creating an initial unit test for a new MVP setup.</Description>
<Author>Billy McCafferty</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>viewInterface</ID>
<ToolTip>Name of the view interface</ToolTip>
<Default>IView</Default>
</Literal>
<Literal>
<ID>presenter</ID>
<ToolTip>Name of the presenter class</ToolTip>
<Default>Presenter</Default>
</Literal>
<Literal>
<ID>mockView</ID>
<ToolTip>Name of the mock view used in the unit test</ToolTip>
<Default>MockView</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[ [Test]
public void TestInitView() {
$viewInterface$ view = new $mockView$();
$presenter$ presenter = new $presenter$(view);
view.AttachPresenter(presenter);
presenter.InitView();
}
private class $mockView$ : $viewInterface$
{
public void AttachPresenter($presenter$ presenter) {
}
}
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>