返回首页

简介
条文规定quot;型号查看Controllerquot;和quot;型号查看Presenterquot;(MVP)是用来描述模式,已被使用一段时间在其他技术领域,但有最近来的彗星#世界中脱颖而出。
型号查看演示模型视图控制器模式的推导。与现代,如Visual Studio中插入到我们所说的观点处理事件的IDE,它使他们有离开,而不是试图实现他们在控制器上。
我挣扎着面向C#中的WinForms的网页上找到一个简单的MVP例子,读条例草案"麦卡弗蒂的MVP在ASP.NET内的优秀文章后,我决定把我的帽子成环。
我要集中精力的代码。对于一个MVP的背景下,我建议你试试这个链接:{A}为什么最有价值球员?
模型视图演示模式是设计抽象的数据显示的数据和相关的动作(例如,保存状态)。 ,在理论上,这应该使测试更容易,更相关,并删除通常在Windows环境之间的数据和形式中的紧耦合。视图
的观点是从System.Windows.Forms的继承的用户控件。它在生活中的作用是显示任何数据,我们感兴趣它不包含任何逻辑引发事件以外应的数据变化和任何处理,特别是关闭本身的情况,如查看。它不关心,如果有人听事件,它只是引发该事件,并已完成了它的目的。查看实现接口公开演示需要了解的领域和活动。

public class UserView : Form, IUserView
该模型
这是一个被操纵数据的代表性。在我的简单的例子,这是一个用户对象。该模型应该实现一个接口(IUserModel)公开,当他们在查看更改演示将更新的领域。{C} 演示
演示模型视图结婚。当第一次调用时,它会更新视图中的所有属性,对应的型号。此外,它结合视图的事件本身的方法。通常情况下,主持人将更新的模型基于视图的变化。一旦用户完成视图的变化,模型应同步将正确地保存下来。演示并不需要一个接口。
public UserPresenter(IUserModel model,IUserView view)

{

    this._model = model;

    this._view = view;

    this.SetViewPropertiesFromModel();

    this.WireUpViewEvents();

}

在我的例子中,我使用反射来遍历视图/模型的属性,并更新相应的字段。反思是缓慢的,但我没有经历过任何有形减慢我的任何Windows应用程序 - 它会很容易通过引用传递给特定的属性被更新的速度开始成为一个问题。将其组合在一起
IUserModel model = new UserModel();

IUserView view = new UserView();

new UserPresenter(model,view);

view.Show();

让球滚动的代码本身很简单,如上图所示。测试
这篇文章中的例子包括一些测试。 ,因为我们的演示预期,而不是​​具体的对象的接口,它允许我们进行依赖注入与存根和嘲笑,我们应该选择。
[Test]

public void DoesViewCorrespondToModel()

{

  StubView stub = new StubView();

  new UserPresenter(this._mock, stub);

在我的测试中,我有一个StubView实现的IUserView接口。然后,我代表模型实例化一个mock对象,并执行该测试。我的测试检查,视图显示模型的,该模型视图更改时更新。你,当然,在一个真实的世界项目包括更多的测试。
我的存根实现事件嘲讽,例如:
public void FireDataChanged()

{

    if (this.DataChanged != null)

    {

        this.DataChanged(null, EventArgs.Empty);

    }

}

在我的测试,我只需拨打"FireDataChanged"重新当用户更改在我看来任何数据从应用程序内会发生什么。一个字上的DataBinding
您可能会奇怪,为什么我去实施演示的代码来更新模型/视图时,微软请我们提供了与数据绑定技术将数据绑定到Windows控件的麻烦。有关数据绑定的伟大的事情是,它消除了写在每一个国家照顾的演示代码,从而加快开发周期的需要。
与数据绑定的问题是,它打破我们紧密耦合的"查看"数据的封装。此外,它不能从一个NUnit的环境中进行测试。然而,仍然需要费力的代码写在演示保持模型与视图同步。我希望我的代码示例为您提供与使用反射将缓解这种疼痛。只要暴露在视图和模型接口所需要的数据,它都应该为你照顾。
我不是一个模式的痴迷者,你会发现给定的时间限制,您需要执行的任务,紧密结合使用绑定的性质是最好的选择 - 不过,我会鼓励从表现抽象的逻辑测试使用尽可能。重构接口
很显然,在发展IVIEW和IModel接口之间共享很多属性定义的文章。事实上,每一个可编辑的数据财产暴露在模型上需要查看实施,因此,您可能要考虑实施额外的接口,被称为是由两个IModel和IVIEW实施ICommonFields。这种方法的好处是,只需要一个编译器的模型和视图坚持其实施一个新的属性添加到一个单一的接口。最后
内的WinForms MVP是一个学习的曲线,我渴望听到任何人认为我所提供的例子可以改善的有建设性的批评。通过分享知识,我们所有的增益。积分
MVP模式已经开发了许多编码一个相当长的时间时期。马丁福勒在一个不错的简洁的形式在其网站上(在文章的顶部链接)的知识。
感谢条例草案"麦卡弗蒂投资这么多时间,他在文章中关于最有价值球员(在文章的顶部链接)在ASP.NET这启发了我提交的WinForms集中的文章中写道。

回答

评论会员:ernestohari 时间:2011/12/06
我是一个简单的例子开始。从MSDN的样品可怕后,这是美好的希望
评论会员:。卡皮尔西辛哈 时间:2011/12/06
是真的necessay在演示中使用的反射吗?需要它只是为了确保松散耦合
评论会员:阿尔乔姆斯米尔诺夫 时间:2011/12/06
我觉得去耦被高估了。毕竟,你脱钩,为了使你的代码更易于维护。当你这样做只是为了去耦,您的代码可以迅速成为一个乱七八糟的服务,接口和抽象工厂,只是为了脱钩调用MessageBox.Show()。回到这篇文章,我想的WinForms数据绑定是这里介绍的反射方法大大优于可维护性。例如,如何实现一个网格绑定到一个项目清单?另一个问题是模型属性对应认为这些要求 - 不只是另一种耦合
的形式?
另一方面,你可以很容易地从你的数据中分离出来的你的看法,如果你使用BindingSource对象。在我看来,这是更好的可维护性,因为你没有改变你的视图或模型的接口,只要你添加/删除从您的类的属性/的。例如,你可以传递一个Order类的实例,你的看法。在查看代码,这个实例将成为相应BindingSource对象的数据源。这个类添加一个属性不会改变的接口,只是执行(您可能需要添加一个TextBox或网格列)

当然,这只是另一种风味的MVP(福勒呼吁监督控制器)
评论会员:会员3083139 时间:2011/12/06
为什么没有在MVP的项目编译时IUserView显示()方法不落实的错误在UserView类
评论会员:?ke4vtw 时间:2011/12/06
我没有调查此密切合作,但我会假设,IUserView是一个WinForm或WebForm中,这两个已经有一个Show()方法,将履行实施合同
评论会员:。奥列格朱可夫 时间:2011/12/06
好和明确的解释!我想通知虽然,在现实世界的条件下,最好是使用一些MVP框架,如,。许多上述任务的自动化,并简化了MVP模式的用法。

奥列格朱可夫
评论会员:马库斯Klieber 时间:2011/12/06
我试图开始一个新的项目一个MVC示例之后,我发现你的代码挺有意思的,即使它是一个MVP。严重的是,它的第一次,我听说过MVP和我很喜欢阅读关于这个问题的一个位的概念。

通过阅读你的代码,我有这个questionning:为什么在图书馆命名空间UserPresenter是因为它在MVP的文件夹?它是故意的吗?

- 修改在10时41分(星期二)五月十五日,2007
评论会员:nji78 时间:2011/12/06
喜克里斯,

我喜欢你的文章,但我觉得你应该更新你关于MVP的介绍。马丁福勒分为两个新模式的MVP。我看到你所说的所谓的"被动浏览"的格局。

的"Smalltalk"语言描述的原始的MVP的视图和模型在MVC模式像之间的通信。

我觉得这是有点混乱,因为我第一次读你的文章,我认为这是原来的MVP,但什么是"orignial"所有这些变种的MVP ... ...

亲切的问候
马库斯
评论会员:cgreen69 时间:2011/12/06
我只是好奇,但似乎这个单一的演示可重用扩展
为什么不使用一个通用的实现SetViewPropertiesFromModel(主持人),
SetModelPropertiesFromView()和每一个模型,它处理。

另外,为什么不只是通过属性名称,而不是通过整体的PropertyInfo收集循环_view_DataChanged找到已更改的值吗?
评论会员:nji78 时间:2011/12/06
Hi和感谢你的问题

我不能完全肯定,我理解你的第一个问题,但这里有云,一个普通的主持人,可以工作在理论假设,所有的主持人不断要做的就是复制/从模型值。通常,这是情况并非如此,现实世界的应用程序有更多的逻辑,所以我不认为一个通用的主持人将适当的主持人。

你的第二个声明是一个很好的建议,假设我们要马上作出反应的变化。我的文章中包含的代码允许所有值要同步特定事件发生时,如用户按下保存和视图中的所有值需要在模型上设置之前保存。

我希望,anwsers您的问题,

克里斯
评论会员:cgreen69 时间:2011/12/06
感谢您的答复。我觉得你做一个更新,当用户点击"保存"按钮的答复是很清楚。
我想知道的是事件,你只是想更新一个字段,数据发生变化时,它将会更有效的,只是改变只有该财产,而不是通过遍历整个集合如下:

考虑下面的类扩展一个EventArgs

<font color="blue">public class</font> <font color="teal">DataChangedEventArgs</font>: <font color="teal">EventArgs</font>

{

	<font color="blue">private string</font> propertyName;

 

	<font color="blue">public string</font> PropertyName

	{

		<font color="blue">get</font>{return propertyName;}

		<font color="blue">set</font>{propertyName = <font color="blue">value</font>;}

	}

 

}

您可以轻松地使用它的属性的名称更改为添加如下数据:

<font color="blue">private void</font> txtEmail_TextChanged(object sender, System.EventArgs e)

{

    <font color="blue">if</font> (<font color="blue">this</font>.DataChanged != <font color="blue">null</font>)

    {

        <font color="teal">DataChangedEventArgs</font> de = ((<font color="teal">DataChangedEventArgs</font>)e);

        de.PropertyName = <font color="brown">"Email"</font>;

        <font color="blue">this</font>.DataChanged(sender, de);

    }

}

那么你会很容易做这样的事情:

的PropertyInfo modelProperty this._model.GetType()的getProperty(viewPropertyName);


评论会员:cdx11356 时间:2011/12/06
的贡献。

我认为你需要立即作出反应事件的情况下,那么你的做法是一个很好的。不过我建议你投发件人对象返回到它的源类型(在你的例子一个文本框),得到的名称,然后使用,通过反射得到的属性,你需要改变模型。
de.PropertyName ="电子邮件",如硬编码字符串的问题是,你必须手动办理,并确保这些仍然符合的基本观点的看法不断改变,在我看来是一种痛苦。
的问候,

克里斯
评论会员:cgreen69 时间:2011/12/06
我新的C#,所以我希望这不是太愚蠢了一个问题,但因为在UserPresenter使用反射,为什么IUSerModel和IUserView接口必须具体UserModel和UserView ?这意味着它为什么定义用户名和电子邮件?请问您有没有一个更通用的IModel和iView中没有定义这些方法的接口。这样,您可以使用任何模型和视图inplements IModel和IVIEW接口的UserPresenter。这将使更多的重用UserPresenter,不是吗?也许您会呼叫演示,而不是在这一点上,它使用的任何意见和模型,你想建立和放在一起。

同样,我appologize如果这个问题是相当"菜鸟",我对C#从Smalltalk的背景。


CDX
评论会员:cdx11356 时间:2011/12/06
。谢谢你的提问

我不熟悉与Smalltalk的,但我会尝试从C锐利的角度来回答。

接口不是通用的,他们的合同,特别是国家一类应该实现什么样的方法和属性。如果接口是通用的,然后根据定义,它会不会需要。你是正确的,它鼓励重用,但将是多余的。

虽然重用是很重要的,我认为,作为一个主持人,包含specfic一个特定的任务,一个很大的逻辑,这是不适合再利用,除非你有许多形式做了非常类似的任务的应用程序,在这种情况下,我想问题的设计您的应用程序。

的问候,

克里斯

&# 160;
评论会员:cgreen69 时间:2011/12/06
其实我与一个特定的项目中,我将交换模型的MVP开始。这就是我喜欢这个框架。我在ST中,我使用一个类似的,它让我放在一起没有很多额外的编码形式迅速。在这样做,我开始考虑抽象重用演示,因为它是使用反射的,它不会出现需要特定的知识,模型或视图。这样做需要更改事件,并可能保存。
从我看到虽然什么,看来,如果我还需要设置/获取的形式,这是我试图避免代码的一部分。

正如你所说,我们都在这个由不同的看法,我只是试图找出如何胶水这东西,而不从模型的推/拉的体力劳动。

其实我的申请形式和堵塞在一个模型符合我还没有定义的接口
一堆任何能力
感谢您的输入,
芯片
评论会员:cdx11356 时间:2011/12/06
对不起,延迟在回答。

我不太清楚,你为什么要避免获取/上形成集建筑,我认为其优雅的方法,但是我没有深入了解您的需求。如果你的反对,是因为它有一个痛苦的代码所有获取和设置,那么我完全同意。我使用ReSharper的,它可以为您构建您的访问器,轻触按钮,我建议你看一看

干杯
评论会员:cgreen69 时间:2011/12/06
克里斯,
我一直在使用这个代码有点刚刚发现一些有趣的事情,想问问你。我已经连接到一个接口和模式的转变,其成员之一的价值模型,我没有看到变化正在推高了的看法。如果这种行为的工作?


芯片
评论会员:陈子明 时间:2011/12/06
是绝对应该的工作。

该模型在我的做法应该总是准确地表述的观点,反之亦然,reflectionis尝试和减少手动连接视图和模型的开发负担。
有可能,这是不恰当的,也许你有一个与控制数百个应用程序,你不需要同步,直到您保存。
假设事件是连接我所期望的方法具有相同的名称看法,在我所提供的代码相同的值。
评论会员:瑞士托马斯 时间:2011/12/06
首先,尼斯的工作!非常感谢你!
"我做了一些事情在我以前的项目中的映射领域,我想与大家分享感激。

为了节省时间浪费在反思,我用CustomAttribute公共领域。如
[ModelField("用户名")]
公共字符串用户名
{
{返回this.txtUserName.Text;}
设置{this.txtUserName.Text =值;}
}
我把里面的UserView类(不IUserView)属性的原因是开发商/分析员开始绘制对话框,他们必须已在头脑中的数据模型。也许并不明显,在代码,但至少他们知道什么假设出现在哪里。然后presentor后者能与此属性数据模型对象中的一个字段。在现实生活中,这是充分开发数据模型时,可以纠正。但是,这种情况非常naturelly
我也使反射扫描只发生在presentor的cstor只能由:

公共UserPresenter(IUserModel模型,IUserView视图)
{
this._model =模型; this._view =视图;
_pInfosOfView = view.GetType()GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.Public); this.SetViewPropertiesFromModel()this.WireUpViewEvents()
}
我发现可以减少使用的BindingFlags属性返回。这是另一个伟大的储蓄,你可以得到免费{S0}
我SetViewPropertiesFromModel也略有改变,所以它仍然是非常通用:
私人无效SetViewPropertiesFromModel()
{
的foreach(在_pInfosOfView的PropertyInfo pInfo)
{
/ /从物业属性模型字段的名称,或直接使用属性的名称。
字符串字段名= string.Empty;
对象[] CAS = pInfo.GetCustomAttributes(typeof运算(ModelFieldAttribute),TRUE);
如果(CAS = NULL cas.Length == 1)
{
ModelFieldAttribute外交部= CAS [0]作为ModelFieldAttribute;
(MFA!= NULL)
{
fieldName的= mfa.FieldName;
}
}
ELSE
{
fieldName的= pInfo.Name;
}
。PV = _model.GetType()对象的getProperty(字段名)的GetValue(_model,NULL); pInfo.SetValue(_view,PV,NULL);}
}
然后你就可以保存调用相同。最好的运气!

简单是最好的
评论会员:Maruis玛莱 时间:2011/12/06
。与反思伟大的工作,为我们节省了很多写代码

我调整
SetModelPropertiesFromView代码()

(modelProperty =空放大器;!放大器; modelProperty.PropertyType.Equals(viewProperty.PropertyType)



- ; (modelProperty =空放大器;!放大器; modelProperty.PropertyType.Equals(viewProperty.PropertyType)
放大器;放大器; modelProperty.CanWrite)


和SetViewPropertiesFromModel()

(modelProperty = NULL!放大器;放大器; modelProperty.PropertyType等于(viewProperty.PropertyType)
放大器;放大器; modelProperty.CanRead)


瑞士{BR致词
}
托马斯
评论会员:c4tes 时间:2011/12/06
很好的例子代码,我喜欢做的一件事是超载,像这样的构造函数:
公共UserPresenter(IUserView视图):(观点,新UserModel())
{
}
公共UserPresenter(IUserModel模型,IUserView视图)
{
this._model =模型;
this._view =视图;
this.SetViewPropertiesFromModel()
this.WireUpViewEvents()
}

然后让我的看法的形式推动创建我的主持人,因为形式的IUserView类型。

私人无效UserView_Load(对象发件人,发送System.EventArgs)
{
新UserPresenter(本)初始化()
}

我通常不直接传递到演示模式,但宁可使用某种形式的服务,将返回我的模型或东西。

甜... ...
评论会员:马克载入 时间:2011/12/06
罗杰和我同意您的总结与asp.net的例子张贴的其他文章。简要关于NUnit的测试。

C -雅
评论会员:cgreen69 时间:2011/12/06
无法下载文件。
评论会员:vl950t 时间:2011/12/06
对不起马克。请再试一次。