Nucleo.NET MVP

The Nucleo.NET MVP framework provides a Model-View-Presenter that is unobtrusive, yet extensible. It can be as easy as you like, or contain a lot of helpful functionality to make working with the pattern easier. It provides a lot of extensibility points, a lot of the features that the ASP.NET MVC framework provides, and it even has a bootstrapper, like you might seen in FubuMvc, for easy setup.

The complete source code and binaries have been moved back to http://nucleo.codeplex.com. Sorry for the confusion.

Quick Overview of the Pattern Implementation

The MVP pattern stands for Model, View, and Presenter pattern. It has three components, the model, or source of data (and in some implementations, logic), the view, or the user presentation layer, and the presenter, also referred to as a controller, which works with the database or other input, processes business rules and logic, and more. The main goal is to keep the user interface logic separate from the business logic.

To illustrate, below is a sample form for a page that displays a list of addresses, and allows a user to select one to edit it, or also create a new one. To start, we have a model that is a container of our data, which may or may not be populated at one time:

public class AddressesModel
{
public AddressRecord SelectedAddress { get; set; }

public IEnumerable<AddressRecord> Addresses { get; set; }
}

Next is our view, which is represented in two pieces. We'll look at the first piece, the view interface, which contains a signature of the view that our presenter can work with.

public interface IAddressesView : IView<AddressesModel>
{
event EventHandler Cancel;
event DataEventHandler<AddressRecord> Save;
event DataEventHandler<int> Selected;
}

Next is the presenter, which responds to the view and loads the model with data accordingly.

public class AddressesPresenter : BasePresenter<IAddressesView>
{
public AddressesPresenter(IAddressesView view)
: base(view)
{
view.Save += View_Save;
view.Selected += View_Selected;
}

private void LoadData()
{
this.View.Model = new AddressesModel
{
Addresses = DataFactory.GetAll<AddressRecord>()
};
}

void View_Save(object sender, DataEventArgs<AddressRecord> e)
{
DataFactory.Add<AddressRecord>(e.Data);

this.LoadData();

this.CurrentContext.EventRegistry.Publish(new PublishedEventDetails(
ListenerCriterion.EntityType<AddressesPresenter>(),
new Dictionary<string, object>
{
{ "Address", e.Data },
{ "Addresses", this.View.Model.Addresses }
}));
}


void View_Selected(object sender, DataEventArgs<int> e)
{
var address = DataFactory.Get<AddressRecord>(e.Data);
this.View.Model = new AddressesModel
{
SelectedAddress = address
};
}

void View_Starting(object sender, EventArgs e)
{
this.LoadData();
}
}

Last is the view implementation, which implements the view contract, but adds some additional logic. This view implementation is in ASP.NET, where our control inherits from a base user control class. It ties the view with the model, defining the model in a generic parameter.

PresenterBinding(typeof(AddressesPresenter))
public partial class Addresses : DemoViewUserControl<AddressesModel>, IAddressesView
{
public event EventHandler Cancel;

public event DataEventHandler<AddressRecord> Save;

public event DataEventHandler<int> Selected;



private int? Key
{
get { return (int?)ViewState["Key"]; }
set { ViewState["Key"] = value; }
}



protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);

if (this.Model == null)
return;

if (this.Model.Addresses != null)
{
this.gvwAddresses.DataSource = this.Model.Addresses;
this.gvwAddresses.DataBind();
}

if (this.Model.SelectedAddress != null)
{
var entity = this.Model.SelectedAddress;
//TODO:Load
}
}

private void OnCancel(EventArgs e)
{
if (Cancel != null)
Cancel(this, e);
}

private void OnSave(DataEventArgs<AddressRecord> e)
{
if (Save != null)
Save(this, e);
}


protected virtual void CancelCommand_Click(object sender, EventArgs e)
{

}

protected virtual void SaveCommand_Click(object sender, EventArgs e)
{
var record = new AddressRecord
{
Key = this.Key ?? 0
};

OnSave(new DataEventArgs<AddressRecord>(record));
}

protected void gvwAddresses_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Select" && e.CommandArgument != null)
{
Selected(this, new DataEventArgs<int>(int.Parse(e.CommandArgument.ToString())));
}
}
}

Notice how the presenter responds to the views event, which are essentially the control's events on the ASP.NET user control. The presenter responds and acts accordingly. The view manages its own UI, and performs all of the data binding. But the presenter is oblivious to the UI, only knowing about the view through an interface.

Last edited Apr 10, 2012 at 10:31 AM by bmains, version 6