Update: The focus of this article is to describe the wiring between the presenter and view which are IMHO the most important thing to get for MVP pattern.
I deliberately melt down presenter and model (by having the presenter itself retrieving the user data in SetUpResultData method instead to call the separate UserRepository component) to avoid having additional service interface into the constructor pattern and making whole L100 example more complex. This is not OK outside of this example context and you shouldn't do it.
I have one more example explaining in more depth MVP pattern which you should read after this article which has more precise description
Introduction
MVP pattern is a type of UI design pattern which enables decoupling of the UI logic without UI representation and by that enables treating code base without consideration on presentation technology used.
One of the commonly stated advantages of this approach is that it enables writing of appropriate test scripts to test UI part of application. MVP enables that without the need of using third party tools which scripts user UI actions and which are fairly easy to use for testing simple actions like button click, but to write a user UI scripts for data manipulation of a grid or some more complex layout. All the logic is inside the presenter and the presenter is talking to view only through the interface, so any mock object could be used for testing as long it implements the view interface
Second advantage is the fact that by decoupling code in presenter level we are enabling reuse of the code logic to totally different presentation technologies. For the example, the same presenter can be used without any problem in both Web Form and Win Form worlds, as long as they implement agreed interface
Third advantage is in the fact that in big companies (like the one I'm working in) to implement your supportive UI code, you are often forced to wait on UX (user experience) people to make the web forms and that sometimes is a very long period. Also the way of communication between products, tech leads and developers is quite often based on some word document based FRD which (from developer perspective) is not the most precise way of specifying requirements. MVP is allowing the tech leads to crystallize results of an FRD to a set of interface definition of a view and that interface could be immediate base for developers to start coding , without waiting for anyone knowing very precise what are the requirements. (Who said TDD? :) )
Illustration 1. Logical view of the MVP pattern.
Short diagram description
View is responsible for visual representation of the form and it contains a presenter in private field. View is implementing interface whose members are approximated and technology striped UI elements of the view. When some event occurs on view is just forwarding it to presenter and it is presenter responsibility to handle the event. Presenter is manipulating view by talking to interface representation of the view. Presenter should never reference directly view members (UI controls). Presenter for his logic operations is using Model which can be persisted state or representation of the object which provides necessary functionality
Implementation procedure:
Create a view by using standard Visual Studio IDE tools.
For my example I will create first a web form with:
- a label on top which would show possible validation error
- Last name, first name, address and a city text boxes
- Search text box with a button
Illustration 2. UI look of the view made by web form
Interface would be created in a separate project called presenter so we could reuse it later to demonstrate usage outside of our web application. Web application should have a reference to the presenter project
Illustration 3. Project structure - separating and referencing presenter
Create a interface as an abstract form of a view with setters only for text boxes (because presenter would fill == set them) and getter property only for searcher (presenter would take that value and perform a search)
Illustration 4. View interface definition
namespace Presenter
{
public interface IUserView
{
string FirstName { set;}
string LastName { set;}
string Address { set;}
string City { set;}
string ErrorMessage { set;}
string SearchCriteria { get;}
}
}
After creation of interface we are implementing it on view by setting and getting UI element values in the property
Illustration 5. View interface implementation
#region IUserView Members
public string FirstName
{
set { firstName.Text = value; }
}
public string LastName
{
set { lastName.Text = value; }
}
public string Address
{
set { address.Text = value; }
}
public string City
{
set { city.Text = value; }
}
public string ErrorMessage
{
set
{
errorMessageLabel.Text = value;
errorMessageLabel.Visible = value != "";
}
}
public string SearchCriteria
{
get { return searchCriteria.Text; }
}
#endregion
Creation of presenter class includes creation of a private field of view interface type and overloading of a constructor to accept that view interface
Illustration 6. Presenter wiring the view
namespace Presenter
{
public class UserViewPresenter
{
IUserView _view;
/// <summary>
/// Initializes a new instance of the UserViewPresenter class.
/// </summary>
/// <param name="view"></param>
public UserViewPresenter(IUserView view)
{
_view = view;
SetCustomerData();
}
Presenter would have to implement business logic, which In our example is finding of a customer for a given code. I’ve created Customer.xml file as D:\Customer.XML
Illustration 7. Exampled data used for sample
Presenter would implement therefore a method named SetCustomerData which task is to find a customer of a given code and update the view with resulting data. In case the entered code is not a valid number presenter would show appropriate message on validator that there is a syntax error. In case there are no contacts with a given code presenter should empty view and show a message that not existing code has been entered.
Illustration 8. Implementation of a presenter logic
public void SetCustomerData()
{
int searchCode;
bool isValid = int.TryParse
(_view.SearchCriteria, out searchCode);
if (isValid)
{
using (DataSet ds = new DataSet())
{
string filePath=string.Format(
"{0}Customers.xml",
AppDomain.CurrentDomain.BaseDirectory
);
ds.ReadXml(filePath);
ds.Tables[0].PrimaryKey
= new DataColumn[]
{ds.Tables[0].Columns["Code"]};
DataRow dr=
ds.Tables[0].Rows.Find
(_view.SearchCriteria);
if (dr != null)
{
_view.FirstName
= dr["FirstName"].ToString();
_view.LastName
= dr["LastName"].ToString();
_view.Address
= dr["Address"].ToString();
_view.City
= dr["City"].ToString();
_view.ErrorMessage
= "";
}
else
{
_view.FirstName = _view.LastName
= _view.Address = _view.City = "";
_view.ErrorMessage
= "Contact not existing";
}
}
}
else
{
_view.FirstName = _view.LastName =
_view.Address = _view.City = "";
_view.ErrorMessage = "Not a valid code!!!";
}
}
Presenter would implement therefore a method named SetCustomerData which task is to find a customer of a given code and update the view with resulting data. In case the entered code is not a valid number presenter would show appropriate message on validator that there is a syntax error. In case there are no contacts with a given code presenter should empty view and show a message that not existing code has been entered.
Illustration 9. Results of an example - Code 1 entered - user shown
Illustration 10. Results of an example - Non existing valid code entered
Illustration 11. Results of an example - Invalid code entered
Now I will show reuse of the presenter for implementing the same logic in totally different technology: WinForm
I'll slightly change the look by switching the places between search criteria and the error message and city would be presented now by ComboList
Illustration 12. UI look of the view made by windows form
Usage of MVP patterns allows me just to implement view interface to win form and to view up presenter by passing to its constructor WinForm and all the same functionality is present without touching a presenter. That's why the presenter is speaking to interface which shouldn't be technology specific
Illustration 13. Wiring up the win form view to the presenter
using System.Windows.Forms;
using Presenter;
namespace WinForm
{
public partial class UserViewForm : Form, IUserView
{
private UserViewPresenter _presenter;
public UserViewForm()
{
InitializeComponent();
_presenter = new UserViewPresenter(this);
_presenter.SetCustomerData();
}
private void searchButton_Click(object sender, System.EventArgs e)
{
_presenter.SetCustomerData();
}
#region IUserView Implementation
// The same implementation like in WebForm case
// Wire up the control text properties to interface members
#endregion
}
}
So the complete solution would be something like:
Illustration 13. Complete project tree
And the result is the same functionality encapsulated in presenter is been reused for a win form
Illustration 14. Results of an example in win form - Existing code entered
Illustration 15. Results of an example in win form - Non existing code entered
Illustration 16. Results of an example in win form - Invalid code entered
Conclusion
MVP is very powerful UI design pattern which has multiple benefits for developing all kinds of user interfaces. That is the reason why CAB (Composite Application Block) is using it as a one of basic UI building principles
Attachments
Source code of the MVP application used in this example