I am trying to learn MVP
It is using web forms in ASP.NET. I have two user controls CurrentTimeView.ascx and MonthViewControl.ascx. The CurrentTimeView displayes time. There is a textbox to add days in the same control. The newly got date is called “resultant date”. When the button is clicked for add days, an event is raised “myBtnAddDaysClickedEvent“.
On the MonthViewControl, there is a label that shows the month of the “resultant date”. At present I am setting a sample value for the variable “monthValueToPass” (since I don’t know how to do it properly). How do I set the value for monthValueToPass variable to make it comply with MVP model?
string monthValueToPass = "TEST";
monthPresenter.SetMonth(monthValueToPass);
The expectation is to create MVP that is easy to do Unit Testing and does not violate MVP architecure.
Note: Though this is a simple example, I am expecting an answer scalablt to databinding in GridView control using MVP and validation mechanisms.
Note: Can view be totally independant of presenter?
Note: Each user control is separate views here
Note: Can there be multiple views for same presenter (like different controls for various users based on thier permisssion?)
GUIDELINES
--COMPLETE CODE--
using System;
public interface ICurrentTimeView
{
//Property of View
DateTime CurrentTime
{
set;
}
//Method of View
void AttachPresenter(CurrentTimePresenter presenter);
}
using System;
public interface IMonthView
{
//Property of View
string MonthName
{
set;
}
//Method of View
//View interface knows the presenter
void AttachPresenter(MonthPresenter presenter);
}
using System;
public class CurrentTimePresenter
{
private ICurrentTimeView view;
//Constructor for prsenter
public CurrentTimePresenter(ICurrentTimeView inputView)
{
if (inputView == null)
{
throw new ArgumentNullException("view may not be null");
}
this.view = inputView;
}
//Method defined in Presenter
public void SetCurrentTime(bool isPostBack)
{
if (!isPostBack)
{
view.CurrentTime = DateTime.Now;
}
}
//Method defined in Presenter
public void AddDays(string daysUnparsed, bool isPageValid)
{
if (isPageValid)
{
view.CurrentTime = DateTime.Now.AddDays(double.Parse(daysUnparsed));
}
}
}
using System;
public class MonthPresenter
{
private IMonthView monthView;
//Constructor for prsenter
public MonthPresenter(IMonthView inputView)
{
if (inputView == null)
{
throw new ArgumentNullException("view may not be null");
}
this.monthView = inputView;
}
//Method defined in Presenter
//How does presenter decides the required value.
public void SetMonth(string monthValueInput)
{
if (!String.IsNullOrEmpty(monthValueInput))
{
monthView.MonthName = monthValueInput;
}
else
{
}
}
}
User Control 1
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CurrentTimeView.ascx.cs" Inherits="Views_CurrentTimeView" %>
using System;
using System.Web.UI;
public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView
{
//1. User control has no method other than view defined method for attaching presenter
//2. Properties has only set method
private CurrentTimePresenter presenter;
// Delegate
public delegate void OnAddDaysClickedDelegate(string strValue);
// Event
public event OnAddDaysClickedDelegate myBtnAddDaysClickedEvent;
//Provision for getting the presenter in User Control from aspx page.
public void AttachPresenter(CurrentTimePresenter presenter)
{
if (presenter == null)
{
throw new ArgumentNullException("presenter may not be null");
}
this.presenter = presenter;
}
//Implement View's Property
public DateTime CurrentTime
{
set
{
//During set of the property, set the control's value
lblCurrentTime.Text = value.ToString();
}
}
//Event Handler in User Control
protected void btnAddDays_OnClick(object sender, EventArgs e)
{
if (presenter == null)
{
throw new FieldAccessException("presenter null");
}
//Ask presenter to do its functionality
presenter.AddDays(txtNumberOfDays.Text, Page.IsValid);
//Raise event
if (myBtnAddDaysClickedEvent != null)
{
myBtnAddDaysClickedEvent(string.Empty);
}
}
}
User Control 2
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="MonthViewControl.ascx.cs" Inherits="Views_MonthViewControl" %>
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Views_MonthViewControl : System.Web.UI.UserControl, IMonthView
{
//1. User control has no method other than view defined method for attaching presenter
//2. Properties has only set method
private MonthPresenter presenter;
//Provision for gettng the presenter in User Control from aspx page.
public void AttachPresenter(MonthPresenter presenter)
{
if (presenter == null)
{
throw new ArgumentNullException("presenter may not be null");
}
this.presenter = presenter;
}
//Implement View's Property
public string MonthName
{
set
{
//During set of the popert, set the control's value
lblMonth.Text = value.ToString();
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
ASPX Page
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ShowMeTheTime.aspx.cs" Inherits="ShowTime" %>
<%@ Register TagPrefix="mvpProject" TagName="CurrentTimeView" Src="Views/CurrentTimeView.ascx" %>
<%@ Register TagPrefix="month" TagName="MonthView" Src="Views/MonthViewControl.ascx" %>
PAGE TITLE
<mvpProject:CurrentTimeView id="ucCtrlcurrentTimeView" runat="server" />
<br />
<br />
<month:MonthView id="ucCtrlMonthView" runat="server" />
</form>
using System;
using System.Web.UI;
public partial class ShowTime : Page
{
CurrentTimePresenter currentTimePresenter;
MonthPresenter monthPresenter;
protected void Page_Load(object sender, EventArgs e)
{
HelperInitCurrentTimeView();
HelperInitMonth();
}
private void HelperInitMonth()
{
//Create presenter
monthPresenter = new MonthPresenter(ucCtrlMonthView);
//Pass the presenter object to user control
ucCtrlMonthView.AttachPresenter(monthPresenter);
}
private void HelperInitCurrentTimeView()
{
//Cretes presenter by passing view(user control) to presenter.
//User control has implemented IView
currentTimePresenter = new CurrentTimePresenter(ucCtrlcurrentTimeView);
//Pass the presenter object to user control
ucCtrlcurrentTimeView.AttachPresenter(currentTimePresenter);
//Call the presenter action to load time in user control.
currentTimePresenter.SetCurrentTime(Page.IsPostBack);
//Event described in User Control ???? Subsribe for it.
ucCtrlcurrentTimeView.myBtnAddDaysClickedEvent += new Views_CurrentTimeView.OnAddDaysClickedDelegate(CurrentTimeViewControl_AddButtonClicked_MainPageHandler);
}
void CurrentTimeViewControl_AddButtonClicked_MainPageHandler(string strValue)
{
string monthValue = "l";
monthPresenter.SetMonth("SAMPLE VALUE");
//myGridCntrl.CurentCharacter = theLetterCtrl.SelectedLetter;
//myGridCntrl.LoadGridValues();
}
}
Some MVP discussions:
Model View Presenter - Guidelines
In MVP where to write validations
MVP - Should views be able to call presenter methods directly or should they always raise events?
MVP - Should the Presenter use Session?
Public Methods or subscribe to View events
MVP pattern, how many views to a presenter?
MVP and UserControls and invocation
ASP.NET Web Forms - Model View Presenter and user controls controls
Restrict violation of architecture - asp.net MVP
Control modification in presentation layer
Decoupling the view, presentation and ASP.NET Web Forms web-forms
I am not experienced with ASP.net but I think I follow the gist of what you are trying to do.
It appears that you are going down to fine a level with your presenter by making presenters for the individual UI elements. In this case the Month and the Time. I would think of it more as ShowTime period. ShowTime has the capability of showing the Month and Time.
To use this with MVP. Then you will need a IShowTimeView that the page will implement. (Not the controls). And then write a ShowTimePresenter that uses IShowTimeView to send and retrieve values.
You will have ShowTime implement the IShowTimeView interface. It will route items like the Time, AddDay events, and the Month to and from the actual controls on the page.
So if I understand your writeup. The sequence of event would be something like this.
The user types in the days to add. The user clicks add days Add Days fires the event which calls a method on the Present to add days. The method in the presenter that add days will make it's calculation and other needed steps. The add days method will then use the View pointer in the Presenter to tell the view to update the Month with the calculated value. The View will then take the calculated value set the correct property on the control.
To do unit testing you need to make a mock object implementing IShowTimeView and use that in place of the actual page object.
TLDR the code.
Here's how I would do it. You say there are 2 controls on the same page. So that can be served by a ContainerVM with references (members) of TimeVM and MonthVM.
This can now be tested without using any views - purely at the presenter level.
Thanks for the inputs. I referred MVP Quickstarts http://msdn.microsoft.com/en-us/library/ff650240.aspx.
Model can raise events
. I think, I should go with that approach. Any thoughts are welcome.Also, I have posted http://forums.asp.net/t/1760921.aspx/1?Model+View+Presenter+Guidelines to collect general rules on MVP.
Quote