简体   繁体   English

事件冒泡和MVP:ASP.NET

[英]Event Bubbling and MVP: ASP.NET

I am trying to learn MVP 我正在尝试学习MVP

It is using web forms in ASP.NET. 它在ASP.NET中使用Web表单。 I have two user controls CurrentTimeView.ascx and MonthViewControl.ascx. 我有两个用户控件CurrentTimeView.ascx和MonthViewControl.ascx。 The CurrentTimeView displayes time. CurrentTimeView显示时间。 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“. 单击按钮添加天数时,将引发一个事件“ myBtnAddDaysClickedEvent”。

On the MonthViewControl, there is a label that shows the month of the “resultant date”. 在MonthViewControl上,有一个标签显示“结果日期”的月份。 At present I am setting a sample value for the variable “monthValueToPass” (since I don't know how to do it properly). 目前,我正在为变量“ monthValueToPass”设置一个样本值(因为我不知道如何正确执行)。 How do I set the value for monthValueToPass variable to make it comply with MVP model? 如何设置monthValueToPass变量的值以使其符合MVP模型?

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. 期望创建易于执行单元测试并且不违反MVP架构的MVP。

Note: Though this is a simple example, I am expecting an answer scalablt to databinding in GridView control using MVP and validation mechanisms. 注意:尽管这是一个简单的示例,但我期望使用MVP和验证机制对GridView控件中的数据绑定有答案。

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 指南

  1. Model View Presenter - 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 用户控制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);
      }
   }     
}

User Control 2 用户控制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 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();
    }
}

Some MVP discussions: 一些MVP讨论:

Model View Presenter - Guidelines 模型视图演示者-准则

In MVP where to write validations 在MVP中写验证的位置

MVP - Should views be able to call presenter methods directly or should they always raise events? MVP-视图应该能够直接调用presenter方法还是应该始终引发事件?

MVP events or property MVP事件或财产

The Model in MVP - Events MVP中的模型-事件

MVP - Should the Presenter use Session? MVP-主持人应该使用会话吗?

Why do Presenters attach to View events instead of View calling Presenter Methods in most ASP.NET MVP implementations? 为什么在大多数ASP.NET MVP实现中,Presenter都附加到View事件,而不是View调用Presenter方法?

Public Methods or subscribe to View events 公开方法或订阅View事件

MVP pattern, how many views to a presenter? MVP模式,对演示者有多少视图?

MVP and UserControls and invocation MVP和UserControls以及调用

ASP.NET Web Forms - Model View Presenter and user controls controls ASP.NET Web窗体-模型视图演示者和用户控件控件

Restrict violation of architecture - asp.net MVP 限制违反体系结构-ASP.NET MVP

Control modification in presentation layer 表示层中的控件修改

Decoupling the view, presentation and ASP.NET Web Forms web-forms 将视图,演示文稿和ASP.NET Web窗体的 Web窗体分离

TLDR the code. TLDR代码。

Here's how I would do it. 这就是我要做的。 You say there are 2 controls on the same page. 您说同一页面上有2个控件。 So that can be served by a ContainerVM with references (members) of TimeVM and MonthVM. 因此,可以由带有TimeVM和MonthVM的引用(成员)的ContainerVM来提供服务。

  1. TimeVM updates a backing property ResultantDate whenever you do your thing. 每当您执行操作时,TimeVM都会更新后备属性ResultantDate。
  2. ContainerVM has subscribed to property-changed notifications for TimeVM.ResultantDate. ContainerVM已订阅TimeVM.ResultantDate的属性更改的通知。 Whenever it receives a change notification, it calls MonthVM.SetMonth() 每当收到更改通知时,它将调用MonthVM.SetMonth()

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 . 我提到了MVP快速入门http://msdn.microsoft.com/en-us/library/ff650240.aspx Model can raise events . 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. 另外,我已经发布了http://forums.asp.net/t/1760921.aspx/1?Model+View+Presenter+Guidelines来收集有关MVP的一般规则。

Quote 引用

Develop Presenter which can communicate with both View and Model. 开发可以与View和Model进行通信的Presenter。 Presenter may only have knowledge of view interfaces. 演示者可能仅具有视图界面的知识。 Even if the concrete view changes, it does not affect presenter. 即使具体视图发生变化,也不会影响演示者。

In the concrete view, control's event handlers will simply call presenter methods or raise events to which presenter would have subscribed. 在具体视图中,控件的事件处理程序将仅调用演示者方法或引发演示者将已订阅的事件。 There should be no presentation rule/logic written in concrete view. 不应以具体的视图编写表示规则/逻辑。

Presenter should have only interface object of model; 演示者应仅具有模型的接口对象; not concrete model. 不是具体的模型。 This is for the ease of Unit Testing 这是为了便于单元测试

View can refer business entities. 视图可以引用业务实体。 However there should no logic written associated with the entity objects. 但是,不应编写与实体对象相关联的逻辑。 It may just pass the entity object to presenter. 它可能只是将实体对象传递给演示者。

View interface should be an abstraction. 视图接口应该是一个抽象。 It should NOT have any control or System.Web reference. 它不应具有任何控件或System.Web参考。 In concrete view, there should be no method other than interface defined methods. 具体而言,接口定义的方法除外。

The "Model" never knows about the concrete view as well as the interface view “模型”从不了解具体视图以及界面视图

"Model" can define and raise events. “模型”可以定义和引发事件。 Presenter can subscribe these events raised by model. 演示者可以订阅这些由模型引发的事件。

Public methods in presenter should be parameterless. 演示者中的公共方法应该是无参数的。 View object should access only parameterless methods of presenter. 视图对象应仅访问演示者的无参数方法。 Another option is view can define events to which the presenter can subscribe. 另一个选项是视图可以定义演示者可以订阅的事件。 Either way, there should be no parameter passing. 无论哪种方式,都不应传递参数。

Since the model has all the required values (to be stored back in database), there is no need to pass any value to model from view (most of the time). 由于模型具有所有必需的值(要存储回数据库中),因此无需(大部分时间)将任何值从视图传递给模型。 Eg when an item is selected in dropdown list only the controls' current index need to be passed to model. 例如,当在下拉列表中选择一项时,只需将控件的当前索引传递给模型。 Then model knows how to get the corresponding domain values. 然后模型知道如何获取相应的域值。 In this case, the view need not pass anything to presenter. 在这种情况下,视图无需将任何内容传递给演示者。 Presenter knows how to get value from view. 演示者知道如何从视图中获取价值。

View may make use of model directly (without using presenter). 视图可以直接使用模型(不使用演示者)。 Eg ObjectDataSource's SelectMethod. 例如ObjectDataSource的SelectMethod。 But controller never knows about the concrete view as well as the interface view. 但是控制器从不知道具体视图和接口视图。

The presenter references the view interface instead of the view's concrete implementation. 演示者引用视图接口,而不是视图的具体实现。 This allows you to replace the actual view with a mock view when running unit tests. 这允许您在运行单元测试时用模拟视图替换实际视图。

I am not experienced with ASP.net but I think I follow the gist of what you are trying to do. 我没有使用ASP.net的经验,但是我想我遵循您尝试做的事情的要旨。

It appears that you are going down to fine a level with your presenter by making presenters for the individual UI elements. 似乎通过为单个UI元素创建演示者,可以与演示者精通一个层次。 In this case the Month and the Time. 在这种情况下,月份和时间。 I would think of it more as ShowTime period. 我将其更多地视为ShowTime时期。 ShowTime has the capability of showing the Month and Time. ShowTime可以显示月份和时间。

To use this with MVP. 将此与MVP一起使用。 Then you will need a IShowTimeView that the page will implement. 然后,您将需要该页面将实现的IShowTimeView。 (Not the controls). (不是控件)。 And then write a ShowTimePresenter that uses IShowTimeView to send and retrieve values. 然后编写一个使用IShowTimeView发送和检索值的ShowTimePresenter。

You will have ShowTime implement the IShowTimeView interface. 您将让ShowTime实现IShowTimeView接口。 It will route items like the Time, AddDay events, and the Month to and from the actual controls on the page. 它将诸如时间,AddDay事件和月份之类的项目往返于页面上的实际控件。

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. 然后,add days方法将使用Presenter中的View指针来告诉视图以计算的值更新Month。 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. 要进行单元测试,您需要制作一个实现IShowTimeView的模拟对象,并使用它代替实际的页面对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM