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

8

编辑:接受Chris Holmes的回答,但如果有更好的方法,我随时准备重构!谢谢!

使用MVP模式制作一些WinForms应用程序,传递实体到另一个视图的最佳方法是什么。

假设我有一个CustomerSearchView/Presenter,在双击时,我想显示CustomerEditView/Presenter。我不希望我的视图了解模型,因此我不能创建一个以ICustomer为参数的构造函数。

我的想法是,

CustomerSearchView创建一个新的CustomerEditView,它创建自己的Presenter。 然后我的CustomerSearchView会执行以下操作:

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

另一个可能的方法是创建一个名为CustomerDTO的类,并创建一个名为CustomerEditView的视图,该视图接受其中一个CustomerDTO。但我认为对于这种简单的任务来说这将是很繁琐的。
很抱歉我的问题很基础,但是我能找到的所有示例都没有涉及到这一点。而且这是一个已经存在的项目,并且目前使用的方法让我感到头痛...
5个回答

4
我不确定你是如何展示你的观点,所以在这里提供具体建议有些困难。这是我以前做过类似事情的方法:
我们让CustomerSearchViewPresenter触发一个事件,比如OpenCustomer(customerId)。(这是假设你的搜索视图只有少量客户数据,并且customerId是其中之一。如果你的搜索视图列出了整个客户对象,那么你可以调用OpenCustomer(customer)。但我不会构建一个搜索视图并允许它填充整个对象...我们保持我们的搜索视图在数据方面轻量级。)
应用程序的其他地方有一个事件处理程序,监听OpenCustomer()事件,并执行创建新的CustomerEditView w/ Presenter的任务(我会委托我的IoC容器为我完成这些工作,所以我不必在任何地方使用"new"关键字)。一旦视图被创建,我们就可以将id(或客户对象)传递给新的CustomerEditView,然后显示它。
负责列出OpenCustomer()事件并执行创建CustomerEditView的类通常是我们应用程序中的某种控制器类。
为了进一步简化这种情况,我另外采用了一种方法:当应用程序或模块启动时,我同时创建了CustomerSearchView(& presenter)和CustomerEditView(& presenter)。当CustomerSearchView需要打开一个客户进行编辑时,CustomerEditView成为OpenCustomer事件的响应者,并将数据加载到自身中,知道如何在其所需的任何容器中显示自己。
所以有多种方法可以做到这一点。

我认为事件是解决这个问题的好方法,正如你所说的,与IoC结合使用可以使代码非常干净。 - pmlarocque

1

这样怎么样:

//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;
    ...
}

我认为你的客户搜索视图应该只委托给它的主持人,你需要执行一个操作。

你在上面将 "presenter" 拼错成了 "presneter"。 - Mark Rogers
好的,那几乎和我说的一样,只是你使用了一个方法而不是属性来设置客户。谢谢。 - pmlarocque
区别在于谁有什么责任。在您的示例中,将客户设置在视图代码中意味着视图知道如何获取客户。这应该留给Presenter来处理。 - Simon Laroche

0

在任何MVP代码中获得自然流畅的关键见解有几个:

  1. 是Presenter推动View,而不是反过来。
  2. 因此1. View不需要知道Presenter的存在。较少的依赖通常意味着更容易维护。

在C#中,我发现事件是解耦Presenter和View的一个很好的资产。有关详细信息,请参阅之前的回答:WinForms中的Model-View-Presenter


错误。在MVP中,视图可能知道存在于Presenter。在许多情况下,视图会创建它自己的Presenter。 - dzendras
1
@dzendras:首先,我并没有说视图不可能知道存在于它的Presenter。我是说视图不需要知道Presenter的存在。这是一个很大的区别,并且承认这一点可以减少依赖关系。其次,是的,视图可以创建它自己的Presenter,但即使这种情况下,也是Presenter主导视图的行为,而不是反过来。 - Johann Gerell
抱歉,我误解了你。那我完全同意你的看法 :) - dzendras

0

我曾经让我的视图与它们的Presenter进行通信,但现在已经改变了。这不符合模式的原始定义(这并不是偏离的原因,只是确保获得这些好处的一个因素)。视图应该尽可能保持简单和少依赖。视图应该通过委托/事件/某种“fire-and-forget”机制与Presenter(任何“观察者”)进行通信。事实上,我专门为MVP引入了控制器来拦截View事件,并将其重新发送给Presenter(很少情况下),或者与系统或Presenter特定的事件总线进行通信,从而使我能够更改用户操作警报机制而不必触及视图。但是要小心事件总线;很快你就会把所有事件都扔进去,应用程序会变得喋喋不休/陷入处理事件的泥潭中,而且事件不是.Net中最快的东西。同步是一个额外的问题,特别是如果您的应用程序需要与用户进行更多的“对话”交互。

应该记住,虽然Presenter通常是视图/进程特定的,但视图(和视图模型)可以被重用;将View与Presenter保持包含/委托关系会强烈耦合View/限制其重用。这可以通过一些DI来减少,但我发现在大多数情况下DI容器是不必要的复杂性(因为我必须知道如何创建对象,而且在创建/测试后有多少次更改语义相似的另一个对象?)。具体依赖仅仅增加了另一层/增加了更多的模糊性/使调试/跟踪变得更加困难。最近一直在追求“简单”,并且大多数情况下更喜欢为大多数应用程序创建自己的工厂/对象创建/ORM映射,因为数据库表/实体之间通常存在“1对1”的关系,并且不需要添加通用第三方ORM工具的复杂性,即使您了解它们的工作原理(这不是重点)。
此外,在MVP中,View观察Model仍然是相当可行的(就像在MVC中一样),所以我不会那么快排除这种可能性。尽管我自己不喜欢这样做,但它并没有“破坏”设计模式。事实上,大约十年前,我开发了类似于MVP的东西,因为我不喜欢MVC组件之间的“循环往复”(View知道Model的情况);我更喜欢所有这些模式(包括MVC)所宣扬的View和Model之间更清晰的分离,并希望保持View尽可能“愚蠢”(观察Model意味着View需要更多处理Model变化的智能)。我最终采用了类似MVVM和策略模式的方法,其中使用模型的“子结构”传递到View中,作为“更改通知器”。这使得所有内容都特定于视图目的,并且具有灵活/可重用的特点(强大的组合)。

0
  1. 我会看一下MS Prism 4,以及他们漂亮的导航界面。还要看一下Silverlight和WCF Navigation。它们做得很好,可以处理像从“脏”表单中提示用户确认并取消等问题。

  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. 使用应用程序范围的事件总线来集中管理事件,如下所示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接