事件冒泡和 MVP:ASP.NET
Posted
技术标签:
【中文标题】事件冒泡和 MVP:ASP.NET【英文标题】:Event Bubbling and MVP: ASP.NET 【发布时间】:2012-02-09 17:41:06 【问题描述】:我正在努力学习 MVP
它在 ASP.NET 中使用 Web 表单。我有两个用户控件 CurrentTimeView.ascx 和 MonthViewControl.ascx。 CurrentTimeView 显示时间。有一个文本框可以在同一控件中添加天数。新获得的日期称为“结果日期”。当单击按钮添加天数时,会引发一个事件“myBtnAddDaysClickedEvent”。
在 MonthViewControl 上,有一个标签显示“结果日期”的月份。目前我正在为变量“monthValueToPass”设置一个示例值(因为我不知道如何正确执行)。 如何设置monthValueToPass变量的值,使其符合MVP模型?
string monthValueToPass = "TEST";
monthPresenter.SetMonth(monthValueToPass);
期望创建易于进行单元测试且不违反 MVP 架构的 MVP。
注意:虽然这是一个简单的示例,但我希望使用 MVP 和验证机制对 GridView 控件中的数据绑定进行扩展。
注意:视图可以完全独立于演示者吗?
注意:这里每个用户控件都是单独的视图
注意:同一演示者是否可以有多个视图(例如根据不同用户的权限对不同的用户进行不同的控制?)
指南
-
Model View Presenter - Guidelines
--完整代码--
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 events or property
The Model in MVP - Events
MVP - Should the Presenter use Session?
Why do Presenters attach to View events instead of View calling Presenter Methods in most ASP.NET MVP implementations?
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 网络表单
【问题讨论】:
tl;dr: 如何创建易于进行单元测试且不违反 MVP 架构的 MVP。另外,view可以完全独立于presenter吗? 【参考方案1】:TLDR 代码。
我会这样做。 你说同一页面上有2个控件。因此,它可以由具有 TimeVM 和 MonthVM 引用(成员)的 ContainerVM 提供服务。
-
每当您执行操作时,TimeVM 都会更新支持属性 ResultantDate。
ContainerVM 已订阅 TimeVM.ResultantDate 的属性更改通知。每当收到更改通知时,它都会调用 MonthVM.SetMonth()
现在可以在不使用任何视图的情况下进行测试 - 纯粹在演示者级别。
【讨论】:
new ContainerVM (TimeVM, MonthVM)
。 ContainerVM对应页面,其他为内部控件的VM。在 ctor 中,ContainerVM 订阅 TimeVM.PropertyChanged。 TimeVM 有一个属性 ResultantDate,它是 databound 。当您更改时间时,ResultantDate 被修改,引发 PropertyChanged 事件(数据绑定)。 ContainerVM 收到通知.. 在其处理程序中,ContainerVM 执行“_monthVM.SetMonth(new-value)”。 Month 控件再次通过数据绑定更新以显示新值。编写代码示例.. 可能需要一些时间:)
@Lijo - 搜索 Josh Smith 在 MVVM 上的 MSDN 文章以了解模式本身的解释。我假设你已经熟悉它了。如果您有一个带有 2 个子视图的视图,则可以在 ViewModel 级别进行镜像,其中一个视图模型持有对 2 个子视图模型的引用。【参考方案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 的一般规则。
引用
开发可以与视图和模型通信的 Presenter。 Presenter 可能只知道视图接口。即使 具体视图变化,不影响presenter。
在具体视图中,控件的事件处理程序将简单地调用 演示者方法或引发演示者将拥有的事件 订阅。不应该有任何表达规则/逻辑写在 具体观点。
Presenter 应该只有模型的接口对象;不是具体的 模型。这是为了便于单元测试
视图可以引用业务实体。但是不应该有逻辑 写与实体对象相关联。它可能只是通过 演示者的实体对象。
视图接口应该是一个抽象。它不应该有任何 控件或 System.Web 参考。具体来看,应该没有 接口定义的方法以外的方法。
“模型”永远不知道具体的视图以及 界面视图
“模型”可以定义和引发事件。演示者可以订阅这些 模型引发的事件。
presenter 中的公共方法应该是无参数的。查看对象 应该只访问presenter的无参数方法。另外一个选择 is view 可以定义演示者可以订阅的事件。任何一个 方式,不应该有参数传递。
由于模型具有所有必需的值(要存储回 数据库),不需要从视图向模型传递任何值(大多数 的时间)。例如。当在下拉列表中选择一个项目时,只有 控件的当前索引需要传递给模型。然后模型知道 如何获取对应的域值。在这种情况下,视图 不需要将任何东西传递给演示者。演示者知道如何获得价值 从视图中。
View 可以直接使用模型(不使用 Presenter)。例如。 ObjectDataSource 的 SelectMethod。但控制器永远不知道 具体视图和界面视图。
演示者引用视图接口而不是视图的 具体实施。这允许您替换实际视图 在运行单元测试时使用模拟视图。
【讨论】:
【参考方案3】:我对 ASP.net 没有经验,但我认为我遵循了您正在尝试做的事情的要点。
通过为各个 UI 元素设置演示者,您似乎可以降低演示者的水平。在这种情况下,月份和时间。我会认为它更像是 ShowTime 时期。 ShowTime 具有显示月份和时间的功能。
将此与 MVP 一起使用。然后,您将需要页面将实现的 IShowTimeView。 (不是控件)。然后编写一个使用 IShowTimeView 发送和检索值的 ShowTimePresenter。
您将让 ShowTime 实现 IShowTimeView 接口。它将时间、AddDay 事件和月份等项目与页面上的实际控件进行路由。
所以如果我理解你的文章。事件的顺序是这样的。
用户键入要添加的天数。 用户点击添加天数 Add Days 触发事件,该事件调用 Present 上的方法来添加天数。 演示者中添加天数的方法将进行计算和其他所需步骤。 然后 add days 方法将使用 Presenter 中的 View 指针告诉视图使用计算值更新月份。 然后,视图将采用计算值在控件上设置正确的属性。
要进行单元测试,您需要创建一个实现 IShowTimeView 的模拟对象并使用它来代替实际的页面对象。
【讨论】:
以上是关于事件冒泡和 MVP:ASP.NET的主要内容,如果未能解决你的问题,请参考以下文章