繁体   English   中英

模型视图演示者,如何在视图之间传递实体?

[英]Model view presenter, how to pass entities between view?

编辑:接受了克里斯·霍姆斯(Chris Holmes)的回应,但如果有人想出更好的方法,请随时准备重构。 谢谢!

用MVP做一些winforms是将实体传递到另一个视图的最佳方法。

假设我有一个CustomerSearchView/Presenter ,在doubleClick上我想显示CustomerEditView/Presenter 我不希望我的视图了解模型,因此无法创建在参数中使用ICustomer的ctor。

我的反应是

CustomerSearchView创建一个新的CustomerEditView ,它创建它自己的演示者。 然后,我的CustomerSearchView将执行以下操作:

var customerEditView = new CustomerEditView();
customerEditView.Presenter.Customer = this.Presenter.SelectedCustomer;

其他可能的方法是使用CustomerDTO类,并使CustomerEditView接受其中的一个CustomerDTO ,但是我认为这很简单。

对基本问题很抱歉,但是我能找到的所有示例都没有达到那个程度,这是一个棕地项目,到目前为止使用的方法让我头疼...

我不知道您是如何显示自己的观点的,因此在此处为您提供具体建议有些困难。 这是我之前做过此类事情的方式:

我们所做的是让CustomerSearchViewPresenter触发类似OpenCustomer(customerId)的事件。 (这是假设您的搜索视图仅包含少量客户数据,而customerId将是其中的一个。如果您的搜索视图具有列出的整个Customer对象,则可以调用OpenCustomer(customer)。但是我不会构建一个搜索视图,并允许其填充整个对象...我们在数据方面保持搜索视图的轻量化。)

该应用程序中的其他地方是一个事件处理程序,该事件处理程序侦听OpenCustomer()事件并执行创建带有Presenter的新CustomerEditView的任务(而我将遵照我的IoC容器为我做这些事情,所以我不这样做)不必在任何地方使用“ new”关键字)。 创建视图后,我们可以将ID(或客户对象)传递给新的CustomerEditView,然后显示它。

负责列出OpenCustomer()事件并执行CustomerEditView创建的此类通常是我们应用程序中的某种Controller类。

为了进一步简化这种情况,我采用了另一种方法:在应用程序或模块启动时,我同时创建了CustomerSearchView(&presenter)和CustomerEditView(&presenter)。 当CustomerSearchView需要打开Customer进行编辑时,CustomerEditView成为OpenCustomer事件的响应者,并将数据加载到自身中,并且知道如何在应该做的任何容器中显示自己。

因此,有多种方法可以做到这一点。

怎么样:

//In CustomerSearchPresenter
var presenter = new CustomerEditPresenter();
var customerEditView = new CustomerEditView(presenter);
presenter.SetCustomer(customer);

//In CustomerEditPresenter
public void SetCustomer(customer)
{
    View.Name = customer.Name;
    View.Id = customer.Id;
    ...
}

考虑到您的客户搜索视图应仅委托给其演示者,您需要执行一个操作。

  1. 我将看一下MS Prism 4及其漂亮的导航界面。 另请参阅Silverlight和WCF导航。 它们做得很好,可以处理诸如提示用户从“脏”表单中进行确认(取消)的操作。

  2. 我还将查看WCF中的PageFunction()文档,以了解如何从另一个“调用”页面并获取信息。

运作方式如下(javascript,抱歉):

用户在客户列表上双击客户:

CustomerList.onDblClick(customerId){

  app.fireEvent('customerEditRequest', id)

}

...

app.onCustomerEditRequest(id){
  this.mainRegion.requestNavigate('customers/edit', id);
}

如果导航到编辑视图成功...

CustomerEditView.onNavigatedTo(context){
  this.model.load(context.parameters.id));
}

CustomerEditView.onSaveButtonClick(){
  this.model.save();
  app.fireEvent('customerEdited', id);
}

...

app.onCustomerEdited(id){
  app.mainRegion.requestNavigate('customerlist', id);
}

您可以通过几种不同的方式来做到这一点:

  1. 从客户列表中向编辑表单发送一个回调函数。 编辑表单将调用它,调用它时您可以执行所需的操作。

  2. 让您听到并回应的“ customerEdited”事件产生编辑表单(无应用程序范围内的总线)

  3. 如图所示,使用应用程序范围的事件总线来集中管理事件。

我曾经让我的观点与他们的演示者进行交流,但是已经脱离了这一点。 它与模式的原始定义不符(这本身不是导致实现这些好处的一个主要因素)。 理想情况下,视图应保持愚蠢状态,并尽可能减少依赖项。 视图应通过代理/事件/某种“即发即弃”机制与演示者(任何“观察者”)进行通信。 实际上,我已经在MVP中引入了一个控制器,专门用于拦截View事件,并(很少)重新触发到Presenter以与Presenter进行通信,或者与系统或Presenter特定的事件总线进行通信-使我能够在不触摸视图的情况下更改用户操作警报机制。 但是必须注意事件总线; 很快,您就会开始在其中引发所有事件,应用在处理事件时会变得闲谈/陷入困境,而事件并不是.Net中最快的事情。 同步是一个额外的问题,尤其是当您的应用需要与用户进行更多“对话式”交互时。

应该记住,尽管Presenter是特定于视图/流程的,但是视图(和视图模型)可以重用; 使View与Presenter处于包含/委派关系中会强烈耦合View /限制其复用。 可以通过一些DI来减少这种情况,但是我发现DI容器在大多数情况下都没有必要的复杂性(因为我仍然必须知道如何创建对象,并且在创建/测试它后,您多久将另一个语义相似的对象更改为一个对象?)。 具体的依赖性无处不在,除了另一层/增加了更多的模糊性/使事情更难以调试/跟踪。 但是最近处于“简单”状态,并且大多数情况下,我更喜欢在大多数应用程序上执行Factory /对象创建/ ORM映射,因为通常使用“一对一”的btw db表/实体,并且需要添加通用的第三方ORM工具的复杂性,即通过通用的上下文/需要为不同的应用程序提供服务,即使您了解它们的工作原理(并非重点),也必须使事情变得比他们需要的更难。

而且,对于View来说,在MVP中观察模型还是很可行的(就像在MVC中一样),因此我不能很快排除这一点。 我不想自己做,但不会“破坏”模式。 实际上,大约十年前,我开发了与MVP类似的东西,因为我不喜欢MVC组件之间的“循环”。 我更喜欢将所有这些模式(包括MVC)都声明为View和Model之间更清晰的分离,以及希望尽可能使View保持沉默(观察Model意味着View需要更多的智能来处理Model更改)。 我最终要做的是像MVVM和策略模式,在其中我使用模型的“子结构”传递给View,充当“变更通知者”。 这样可以使所有视图都具有特定目的且灵活/可重复使用(强壮的组合)。

要获得任何MVP代码中的自然流程,有一些关键的见解:

  1. 是由主持人驱动视图,而不是相反。
  2. 由于1.视图不需要知道演示者的存在。 较少的依赖关系通常意味着更容易维护。

在C#中,当将演示者与视图分离时,我发现事件是一项重要资产。 先前的答案中有更多详细信息: WinForms中的Model-View-Presenter

暂无
暂无

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

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