我正在努力学习MVP
它在ASP.NET中使用Web表单。我有两个用户控件CurrentTimeView.ascx和MonthViewControl.ascx。 CurrentTimeView显示时间。有一个文本框可以在同一个控件中添加天数。新获得的日期称为“结果日期”。单击按钮添加天数时,会引发一个事件“myBtnAddDaysClickedEvent”。
在MonthViewControl上,有一个标签显示“结果日期”的月份。目前我正在为变量“monthValueToPass”设置一个样本值(因为我不知道如何正确地做到这一点)。 如何设置monthValueToPass变量的值以使其符合MVP模型?
string monthValueToPass = "TEST";
monthPresenter.SetMonth(monthValueToPass);
期望创建易于进行单元测试的MVP并且不违反MVP架构。
注意:虽然这是一个简单的例子,但我期待使用MVP和验证机制在GridView控件中进行数据绑定的scalablt答案。
注意:可以查看完全独立的演示者吗?
注意:每个用户控件都是单独的视图
注意:同一个演示者可以有多个视图(例如,基于他们的许可对不同用户的不同控制吗?)
指南
- 完整代码 -
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
{
}
}
}
用户控制1
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CurrentTimeView.ascx.cs" Inherits="Views_CurrentTimeView" %>
<asp:Label id="lblMessage" runat="server" /><br />
<asp:Label id="lblCurrentTime" runat="server" /><br />
<br />
<asp:TextBox id="txtNumberOfDays" runat="server" />
<asp:Button id="btnAddDays" Text="Add Days" runat="server" OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />
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);
}
}
}
用户控制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 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" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>PAGE TITLE </title>
</head>
<body>
<form id="form1" runat="server">
<mvpProject:CurrentTimeView id="ucCtrlcurrentTimeView" runat="server"
/>
<br />
<br />
<month:MonthView id="ucCtrlMonthView" runat="server" />
</form>
</body>
</html>
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();
}
}
一些MVP讨论:
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 对照
Restrict violation of architecture - asp.net MVP
Control modification in presentation layer
Decoupling the view, presentation and ASP.NET Web Forms web的表单
答案 0 :(得分:2)
TLDR代码。
这是我将如何做到的。 你说同一页面上有2个控件。这可以由具有TimeVM和MonthVM的引用(成员)的ContainerVM提供。
现在可以在不使用任何视图的情况下测试 - 纯粹在演示者级别。
答案 1 :(得分:2)
感谢您的投入。我提到了MVP快速入门http://msdn.microsoft.com/en-us/library/ff650240.aspx。 Model can raise events
。我想,我应该采用这种方法。欢迎任何想法。
另外,我发布了http://forums.asp.net/t/1760921.aspx/1?Model+View+Presenter+Guidelines来收集关于MVP的一般规则。
引用
开发可与View和Model通信的Presenter。 演示者可能只具有视图界面的知识。即使是 具体视图更改,它不会影响演示者。
在具体视图中,控件的事件处理程序将简单地调用 演示者方法或引发演示者可能拥有的事件 认购。应该没有写入的表示规则/逻辑 具体观点。
Presenter应该只有模型的接口对象;不具体 模型。这是为了便于单元测试
视图可以引用业务实体。但是应该没有逻辑 写入与实体对象相关联。它可能只是通过了 实体对象为演示者。
View界面应该是一个抽象。它不应该有任何 控件或System.Web参考。具体来说,应该没有 接口定义方法以外的方法。
“模型”从不知道具体的观点以及 界面视图
“模型”可以定义和提升事件。演示者可以订阅这些 模特提出的事件。
演示者中的公共方法应该是无参数的。查看对象 应该只访问演示者的无参数方法。另外一个选项 视图可以定义演示者可以订阅的事件。或 方式,应该没有参数传递。
由于模型具有所有必需的值(存储回来 数据库),没有必要从视图中传递任何值到模型(大多数 的时间)。例如。当只在下拉列表中选择一个项目时 控件的当前索引需要传递给模型。然后模型知道 如何获取相应的域值。在这种情况下,视图 不需要传递任何东西给主持人。 Presenter知道如何获得价值 从视图。
View可以直接使用模型(不使用演示者)。例如。 ObjectDataSource的SelectMethod。但是控制器永远都不知道 具体视图以及界面视图。
演示者引用视图界面而不是视图 具体实施。这允许您替换实际视图 在运行单元测试时使用模拟视图。
答案 2 :(得分:1)
我对ASP.net没有经验,但我认为我遵循你想要做的事情的要点。
通过为各个UI元素制作演示者,您似乎可以与演示者达成一致。在这种情况下,月份和时间。我会把它想象成ShowTime时期。 ShowTime具有显示月份和时间的功能。
将此与MVP一起使用。然后,您将需要一个页面将实现的IShowTimeView。 (不是控件)。然后编写一个使用IShowTimeView发送和检索值的ShowTimePresenter。
您将使ShowTime实现IShowTimeView接口。它会将Time,AddDay事件和Month等项目与页面上的实际控件相关联。
所以,如果我理解你的写作。事件的顺序就是这样的。
用户输入要添加的日期。 用户点击添加天数 Add Days会触发调用Present上的方法以添加天数的事件。 演示者中添加天数的方法将进行计算和其他所需步骤。 然后,add days方法将使用Presenter中的View指针指示视图使用计算值更新Month。 然后,View将计算值设置为控件上的正确属性。
要进行单元测试,您需要创建一个实现IShowTimeView的模拟对象,并使用它来代替实际的页面对象。