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:
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:
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:
public abstract class GenericPresenter<V> : BasicPresenter
where V : class, IBasicView
{
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.
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.
public abstract class MVPUserControl<P, V> : UserControl, IMVPControl
where P : GenericPresenter<V>, new()
where V : class, IBasicView
{
private P presenter;
public MVPUserControl()
{
this.presenter = new P().BindToView(this as V) as P;
}
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
and V
as arguments, the control forces the developer to implement the P
as GenericPresenter<V>
, V
being the same one specified in UserControl<P, V>
so design time typing would be complete unless we cannot also constrain the control to implement the type V
(the according view interface) since V
is just a type argument:
class Foo<V> : V
I guess we can live with both if the solution is tightly designed.
Finally, the BasicPresenter
is the abstract that the generic presenter extends:
public abstract class BasicPresenter
{
private IBasicView view;
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;
}
internal protected IBasicView ViewInternal
{
get
{
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>
{
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)
{
}
}
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
{
protected override void AddedControl(Control control, int index)
{
base.AddedControl(control, index);
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:
public abstract class BasicPresenter
{
private BasicPresenter parent;
private IList<BasicPresenter> children;
public event EventHandler ParentChanged;
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);
}
}
}
}
public BasicPresenter Root
{
get { return (null == this.parent) ? this : this.parent.Root; }
}
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.