Ease your Model View Presenter
This article describes how MVP integration in ASP.NET 2.0 can become easier with .NET generics and Hierarchy in Presenters
Note: The demo project is Web Application Project so if you don’t have VS 2005 SP1 installed, you should get at least the Web Application Project support.
Introduction
Model View Presenter can be an addiction for the way in which you design your ASP.NET applications. I mean once you tried it, you'll wonder why the Microsoft guys haven’t thought about an ASP.NET integrated approach until ASP.NET MVC framework (not MVP, true, but much related pattern concept).
What brings the MVP? (This is the good) Logic testability, separation of concerns, etc; I personally like it also because it involves a kind of code order and clearness.
What else? (This is the bad.) It almost kills the concept of Rapid Application Development (aka RAD); I mean it is pretty time consuming for a developer because for each Page
/UserControl
which follows MVP, she/he has to code the view-presenter binding, various initializations and (yes, the toughest one) all those properties that need to be passed from/to the view.
That’s why I propose to make the pattern a little bit more pleasant to be implemented by having MVP with .NET generics and Hierarchy in Presenters.
MVP Prerequisites
Even if I believe that the complexity of the article is not high, an MVP tutorial should be consulted before, since this one suggests a variation of the pattern not the pattern itself. Have a peek at some of these great articles if you are not familiar with MVP:
- ModelViewPresenter.aspx (also has a suggestive picture)
- http://msdn2.microsoft.com/en-us/magazine/cc188690.aspx
- http://www.dnrtv.com/default.aspx?showNum=14 (video presentation)
Briefly explained, the MVP pattern applied in ASP.NET splits in half the implementation of how we used to do the code behind: one part, just the UI interaction, remains in the code behind and gets the name of View, and all the logic gets moved into a separate layer and takes the name of Presenter.
Now, imagine a soldier in the middle of a war that reports fight status and events to a commander who’s watching from behind; the commander upon that information consumes logic, takes decisions and finally makes the soldier update. In this example (analog to MVP), it’s important that the soldier doesn’t do the commander’s job and the other way around; I guess it's pretty obvious who is View and who is Presenter, as for who is the Model, it could be made of arsenal, maps, etc.
Note: Because of the lack of space and for simplicity reasons, I removed some of the member definition comments when used in this article but you can find all that in the downloadable solution.
MVP with .NET Generics
The generics seem to be useful everywhere, why not use them here too? Why not encapsulate common behavior in abstracts but at the same time avoid strong typing issues using them?
A presenter depends on a view to communicate with:
/// <summary>
/// Base presenter class that strong types the bound view.
/// </summary>
/// <typeparam name="V">View type argument.</typeparam>
public abstract class GenericPresenter<V> : BasicPresenter
where V : class, IBasicView
{
}
You’ll say: What’s the benefit? Or maybe why use generics like this since it seems that we already have a basic presenter to inherit from? Because when this class is inherited from, the developer automatically benefits from the common view implementation within the presenter; in this case, as the view declaration:
/// <summary>
/// Base presenter class that strong types the bound view.
/// </summary>
/// <typeparam name="V">View type argument.</typeparam>
public abstract class GenericPresenter<V> : BasicPresenter
where V : class, IBasicView
{
// Properties
/// <summary>
/// Gets the bound view.
/// </summary>
/// <value>The bound view.</value>
protected V View
{
get { return base.ViewInternal as V; }
}
}
We will see later that ViewInternal
is the most basic view (IBasicView
) that in here is converted to the right type.
/// <summary>
/// Root View meaning.
/// </summary>
public interface IBasicView
{
}
IBasicView
interface is almost trivial since it doesn’t have any member defined but its purpose is just for the views to have a common root (the presenter’s V
type argument to have a meaning).
The control part gets a little bit more complicated, but not too much. In this example, I used a UserControl
but it may as well be a Page
or a simple server control.
/// <summary>
/// Base MVP user control.
/// </summary>
/// <typeparam name="P">The presenter type argument.</typeparam>
/// <typeparam name="V">The view type argument.</typeparam>
public abstract class MVPUserControl<P, V> : UserControl, IMVPControl
where P : GenericPresenter<V>, new()
where V : class, IBasicView
{
// Fields
private P presenter;
// Methods
public MVPUserControl()
{
this.presenter = new P().BindToView(this as V) as P;
}
// Properties
protected P Presenter
{
get { return this.presenter; }
}
}
I think you already have some ideas about reusable custom code you can add in here…
Anyway, the implementation has also 2 small minuses:
- The view cannot be passed to the presenter constructor since the generic type arguments supports just the parameterless constructor constraint
class Foo<V> where V : new()
Having the types
P
andV
as arguments, the control forces the developer to implement theP
asGenericPresenter<V>
,V
being the same one specified inUserControl<P, V>
so design time typing would be complete unless we cannot also constrain the control to implement the typeV
(the according view interface) sinceV
is just a type argument:class Foo<V> : V // generates compiler error
I guess we can live with both if the solution is tightly designed.
Finally, the BasicPresenter
is the abstract that the generic presenter extends:
/// <summary>
/// Basic view functionality (view binding and hierarchy).
/// </summary>
public abstract class BasicPresenter
{
// Fields
private IBasicView view;
// Methods
/// <summary>
/// Binds to view.
/// </summary>
/// <param name="view">The view.</param>
/// <returns>This.</returns>
public BasicPresenter BindToView(IBasicView view)
{
if (null != this.view)
{
throw new InvalidOperationException("The presenter is already bound.");
}
this.view = view;
if (null == this.view)
{
throw new ArgumentNullException("view");
}
return this;
}
// Properties
internal protected IBasicView ViewInternal
{
get
{
// ensure presenter binding even with a small
// performance penalty cost due to view instance checking
if (null == this.view)
{
throw new InvalidOperationException("Use of unbound presenter.");
}
return this.view;
}
}
}
Hierarchy in Presenters
I was many times in the situation of encapsulating a lot of logic in UI components (User Controls, Server Controls) many times for the sake of reusability, sometimes having heavy hierarchical trees of Parent/Child controls.
How to integrate the MVP pattern in this kind of situation?
For better understanding of the problem, here is an example: suppose in a Page
, a date time string format combo is fired for a SelectedIndexChanged
event that is transformed into a presenter event like DateTimeFormatChanged
(since we have the separation of concerns); suppose our page contains a Content user control which contains an EmployeeList user control. All of them have presenters since they need to be reused on several pages. How can the presenter of the EmployeeList subscribe to the DateTimeFormatChanged
event so it can update its date time values for ‘BirthDate
’ and ‘HireDate
’ of the employee? The option would be to use the control hierarchy in order to reach the Page
that would give us its presenter to register for the event; imagine we have a lot of this kind of controls that needs to update the date time format when it is changed, so we have to do the same thing for all. Don’t you think there is an overhead in this approach?
What if instead we would have in the employee list presenter something like this:
public class EmployeesListPresenter : GenericPresenter<IEmployeesListView>
{
// Methods
public override BasicPresenter Initialize()
{
base.Initialize();
if ((null != this.Root) && (this.Root is IDateTimeFormatChanger))
{
(this.Root as IDateTimeFormatChanger).DateTimeFormatChanged +=
new DateTimeFormatEventHandler
(DaysOfWeekPresenter_DateTimeFormatChanged);
}
return this;
}
private void DaysOfWeekPresenter_DateTimeFormatChanged
(object sender, DateTimeFormatEventArgs e)
{
// handle datetime format changed
}
}
This would simplify the situation; Root and Presenter are possible because we hierarchize the presenters by “copying” the control hierarchy so when a control becomes the parent of another, it also register its presenter as a parent to the child control presenter.
public abstract class MVPUserControl<P, V> : UserControl, IMVPControl
where P : GenericPresenter<V>, new()
where V : class, IBasicView
{
// Methods
protected override void AddedControl(Control control, int index)
{
base.AddedControl(control, index);
// suppose we have
// <res:MVPControl ID="parent">
// <div>
// <res:MVPControl ID="child">
// the current event is raised in the parent just for the div
// so we have to find the child so not to loose it
foreach (IMVPControl mvpControl in this.FindMVPChildren(control))
{
mvpControl.Hierarchize(this.presenter);
}
}
protected override void RemovedControl(Control control)
{
base.RemovedControl(control);
foreach (IMVPControl mvpControl in this.FindMVPChildren(control))
{
mvpControl.Hierarchize(null);
}
}
private IEnumerable<IMVPControl> FindMVPChildren(Control control)
{
if (null != control)
{
if (control is IMVPControl)
{
yield return (control as IMVPControl);
}
else if (0 < control.Controls.Count)
{
foreach (Control childControl in control.Controls)
{
foreach (IMVPControl mvpControl in this.FindMVPChildren(childControl))
{
yield return mvpControl;
}
}
}
}
}
#region IMVPControl Members
public void Hierarchize(BasicPresenter shouldBeParent)
{
this.presenter.Parent = shouldBeParent;
}
#endregion
}
The code is pretty straight forward: when a child control is added, we find the first level of child controls that implements the MVP hierarchization so they can receive their parent.
The same methods can be overridden in a Page
or in a server control, so as long as the IMVPControl
is implemented, the control receives hierarchy.
The Parent/Child support should be somewhere deep inside the presenters:
/// <summary>
/// Basic view functionality (view binding and hierarchy).
/// </summary>
public abstract class BasicPresenter
{
// Fields
private BasicPresenter parent;
private IList<BasicPresenter> children;
// Events
/// <summary>
/// Occurs when the presenter parent was just changed.
/// </summary>
public event EventHandler ParentChanged;
// Properties
/// <summary>
/// Gets or sets the presenter parent.
/// </summary>
/// <value>The presenter parent.</value>
public virtual BasicPresenter Parent
{
get { return this.parent; }
set
{
if (value != this.parent)
{
if (null != this.parent)
{
this.parent.Children.Remove(this as BasicPresenter);
}
this.parent = value;
if (null != this.parent)
{
this.parent.Children.Add(this as BasicPresenter);
}
if (null != this.ParentChanged)
{
this.ParentChanged(this, EventArgs.Empty);
}
}
}
}
/// <summary>
/// Gets the root presenter.
/// </summary>
/// <value>The root presenter.</value>
public BasicPresenter Root
{
get { return (null == this.parent) ? this : this.parent.Root; }
}
/// <summary>
/// Gets the presenter children.
/// </summary>
/// <value>The presenter children.</value>
public IList<BasicPresenter> Children
{
get
{
if (null == this.children)
{
this.children = new List<BasicPresenter>();
}
return this.children;
}
}
}
Conclusion
The use of generics can boost the power of inheritance and also makes your design elegant, so MVP shouldn’t be an exception. Moreover, if the application requirements ask for complex user controls and you would also like MVP, there are simple solutions to make your life easier.