ASP.NET Web Forms MVP

I like coding to patterns. And one pattern I really like is the Model View Presenter (MVP) pattern. Whilst the Patterns and Practices group’s implementation of this pattern (Web Client Software Factory) is good for big applications with a multitude of business modules, it is too heavy for smaller applications. This is where the ASP.NET Web Forms MVP project fills a niche nicely.

Put together by an Australian duo (Tatham Oddie and Damian Edwards), ASP.NET Web Forms MVP is a very nice implementation of the MVP pattern using the WebForms flavour of ASP.NET. With ASP.NET MVC all the rage these days, I like the fact that these guys went off and did their own thing, injecting new life into the WebForms universe. And they have incorporated messaging! Now that is really handy and feels like possible lessons learnt from some of the XAML technologies.

I have put together a really simple example to give you a taste. The charter I set for this example was to showcase the typical lifecycle of a request, starting at the View, going via the Presenter and hitting a data layer to retrieve some data from an XML file and display it in a grid (back on the View). There’s no paging or sorting on the Grid – unnecessary complexity for this style of example.

In the download code, you will find a solution with the following structure:

I have deliberately used the same prefix for the Logic and DataAccess projects (i.e. the name of the web project) as I have relied on convention for Presenter discovery.

Firstly, I need to create a model for my page:

public class DisplayAlbumsModel
{
	public List<Album> Albums { get; set; }
	public class Album
	{
		public string Id { get; set; }
		public string Artist { get; set; }
		public string Title { get; set; }
	}
}

This will provide the data context which I can hydrate in my Presenter and make available to my View.

Speaking of Views, I need an interface for the View:

public interface IDisplayAlbumsView : IView<DisplayAlbumsModel>
{
	event EventHandler RetrieveAlbums;
}

One event will do. This will be raised by the View to the Presenter, which will subscribe to it in its Load method. With composing components as the desired approach, we will now implement that View as a user control, over in the Webapp project. We will then chuck that user control on a very basic, bare-bones web page. The user control inherits from MvpUserControl<DisplayAlbumsModel>, strongly typed to my Model. It also implements IDisplayAlbumsView, raising the member event RetrieveAlbums in the Page_Load event handler:

public partial class DisplayAlbumsControl : MvpUserControl<DisplayAlbumsModel>, IDisplayAlbumsView
{
	protected void Page_Load(object sender, EventArgs e)
	{
		if (!IsPostBack)
		{
			RetrieveAlbums(this, EventArgs.Empty);
			this.AlbumsGridView.DataSource = Model.Albums;
			this.AlbumsGridView.DataBind();
		}
	}

	public event EventHandler RetrieveAlbums;

	protected void AlbumsGridViewRowcommand(object sender, GridViewCommandEventArgs e)
	{
		// do stuff here. E.g. redirect to a page with details about the selected album.
	}
}

Finally, over to the Presenter back in the Logic project, which inherits from the WebFormsMvp.Presenter class, strongly typed to our View:

public class DisplayAlbumsPresenter : Presenter<IDisplayAlbumsView>
{
	private readonly ChinookDbHelper dataAccessHelper;

	public DisplayAlbumsPresenter(IDisplayAlbumsView view) : base(view)
	{
		dataAccessHelper = new ChinookDbHelper();
		View.RetrieveAlbums += View_RetrieveAlbums;
	}

	void View_RetrieveAlbums(object sender, EventArgs e)
	{
		List<DisplayAlbumsModel.Album> albums = new List<DisplayAlbumsModel.Album>(); // a collection of Domain objects.
		var albumsData = dataAccessHelper.GetAlbums(); // get the tuples from the Data Layer

		//  Transform the raw data to a collection of Domain objects.
		foreach (var album in albumsData)
		{
			albums.Add(new DisplayAlbumsModel.Album {Artist = album.Item3, Id = album.Item1, Title = album.Item2});
		}

		View.Model.Albums = albums;
	}
}

The presenter discovery is done by convention. To demonstrate what the framework looks for, all I have to do is add a Typo to the Presenter’s namespace. The stack trace tells the whole story. First it looks for the PresenterBinding attribute (more on that later). After that, it searches for it in certain namespaces:

AttributeBasedPresenterDiscoveryStrategy:
– could not find a [PresenterBinding] attribute on view instance ASP.controls_displayalbumscontrol_ascx

ConventionBasedPresenterDiscoveryStrategy:
– could not find a presenter with type name WebFormsMvpSimplePage.DisplayAlbumsPresenter
– could not find a presenter with type name WebFormsMvpSimplePage.Logic.Presenters.DisplayAlbumsPresenter
– could not find a presenter with type name WebFormsMvpSimplePage.Presenters.DisplayAlbumsPresenter
– could not find a presenter with type name WebFormsMvpSimplePage.Logic.DisplayAlbumsPresenter
– could not find a presenter with type name WebFormsMvpSimplePage.DisplayAlbums
– could not find a presenter with type name WebFormsMvpSimplePage.Logic.Presenters.DisplayAlbums
– could not find a presenter with type name WebFormsMvpSimplePage.Presenters.DisplayAlbums
– could not find a presenter with type name WebFormsMvpSimplePage.Logic.DisplayAlbums

So we can see how managing naming convention can be used to wire up the components of the MVP pattern.

If you want to use the PresenterBinding attribute for Presenter discovery, that is easily implemented by decorating the usercontrol class as follows:

[PresenterBinding(typeof(DisplayAlbumsPresenter), 
ViewType = typeof(IView<DisplayAlbumsModel>), 
BindingMode = BindingMode.Default)]

The attribute is the WebFormsMvp.PresenterBindingAttribute which has 3 properties: BindingMode, PresenterType and ViewType. Further examination of that attribute is beyond the scope of this post.

I’d like to thank Oddie and Edwards for this cool and light framework. Perfect for the scenario where you don’t want the overhead of the Web Client Software Factory, but you want the trimmings, familiarity and RAD aspects of Web Forms, plus the testability that the MVP pattern provides.

Download Code

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>