Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / C#

Ease your Model View Presenter

Rate me:
Please Sign up or sign in to vote.
3.96/5 (38 votes)
15 Dec 2008CPOL6 min read 46.5K   586   47   7
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:

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:

C#
/// <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:

C#
/// <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.

C#
/// <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.

C#
/// <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:

  1. The view cannot be passed to the presenter constructor since the generic type arguments supports just the parameterless constructor constraint
    C#
    class Foo<V> where V : new()
  2. 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:
    C#
    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:

C#
/// <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:

C#
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.

C#
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:

C#
/// <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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralSubject good, format bad Pin
Not Active17-Apr-08 2:27
mentorNot Active17-Apr-08 2:27 
The subject is good, however, the formatting, text colors, size, etc. make it difficult to read.


only two letters away from being an asset

GeneralRe: Subject good, format bad Pin
alex2317-Apr-08 19:17
alex2317-Apr-08 19:17 
GeneralRe: Subject good, format bad Pin
Not Active18-Apr-08 1:57
mentorNot Active18-Apr-08 1:57 
GeneralRe: Subject good, format bad Pin
alex2318-Apr-08 9:52
alex2318-Apr-08 9:52 
GeneralIntresting article Pin
sebastian_moroianu10-Mar-08 22:45
sebastian_moroianu10-Mar-08 22:45 
GeneralGenericity and MVP rocks! Pin
Stefan Totoescu9-Mar-08 20:56
Stefan Totoescu9-Mar-08 20:56 
GeneralGood article Pin
ra00l7-Mar-08 19:26
ra00l7-Mar-08 19:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.