我在 WinForms 中涉足了 MVP,有很多问题需要解决。大多数问题都来自于您在 VS 中的事实,并且能够使用表单设计器轻松设计表单真是太好了。
我尝试了一个从广泛使用的 WebForms MVP 项目开发的 WinForms MVP API,但是由于表单的隐藏文件代码使用了泛型(例如公共类 TheForm:UserControl),你失去了设计表单的能力,因为设计师知道如何处理泛型。
我最终选择了核心接口 IPresenter、IView、IViewModel。即使我没有添加任何额外的属性,我也总是为特定实现创建一个中间接口,主要是因为当我确实想要添加额外的属性时,它更容易适应以后的更改。IPresenter 采用 IView 类型的协变泛型类型所以在继承链中,我可以制作特定子视图类型的演示者。最后创建一个对话框是通过实例化一个 Presenter 并调用 Show 来完成的:
ISomePresenter<ISomeView> somePresenter = new SomeFactory.GetSomePresenter();
somePresenter.Show();
我的视图包含 IViewModel 的副本:
public void Show()
{
ISomeView theView = new V();
theView.ViewModel = new SomePresenterViewModel();
.
.
.
}
不回到原来的问题...... SampleView 无法了解 ISampleViewModel,因此如果不将演员表放在某个地方,就不可能对 ViewModel 进行标准数据绑定。这在我开发所有这些东西的项目中失控了,人们在事件处理程序以及 BindingSource 向导中到处进行转换。MVP的全部意义都丢失了。
因此,现在我对如何处理事件以及将控件设置为公共属性(以及随附的 ISampleView 属性)变得非常严格,以便演示者可以看到它们,或者只是创建重复事件以重新触发事件由演示者接听我一直在思考整个数据绑定难题。确实,唯一的方法是在没有设计人员支持的情况下完成设计人员在 Presenter 内的代码中所做的一切。也许使用 Designer 在 .designer.cs 文件中获取自动生成的代码,但将所有代码剪切到 Presenter 中。也许做一次以获得正确的语法等,然后抨击一些样板代码或根据生成的内容创建一个片段。您仍然需要访问视图上的实际控件才能指定绑定,因此,还要向返回控件实例的 ISampleView 添加一个属性。另外,我建议将 BindingSource 实例也放在 Presenter 中,或者至少将 Presenter 保存到的其他一些类中。
我喜欢尽可能多地使用 Designer,但有时您需要中断。正如我所说,CodePlex 上的 WinForms MVP 项目很棒,但所有表单设计都是在代码中完成的。在我的场景中,只有 DataBinding 需要在代码中完成,这实际上并不是视觉上的东西,因此更容易处理。
此外,作为旁注,用户 NotifyPropertyWeaver (IL Weaving) 支持完整的数据绑定。非常棒的是,您可以在视图模型中创建自动属性,从而使您的代码简洁且更具可读性,而无需在每个属性上调用 NotifyPropertyChanging 等。IL Weaving with Fody 在最终构建输出步骤之前完成所有后编译。非常方便。
无论如何,我希望围绕这个问题的概念头脑转储对某人有价值。我花了很长时间整理它,但它对我来说效果很好。
史蒂夫
编辑 2014-04-23
你知道吗,.NET 数据绑定是一个巨大的痛苦。最近在一个项目中,我们刚刚为特定控件滚动了自己的数据绑定代码,因为这一切都很难处理。
重新考虑我最初的回复以及最近的经验,核心模型应该完全分开。我倾向于创建一个我称之为 ViewModel 的东西,它与数据库对话并且是 DataBindable 并且被 View 看到。数据绑定让我非常伤心,尤其是在处理控制事件时,比如 DateTimePicker 的 ValueChanged。在一种情况下,我有一个开始和结束日期选择器以及一个复选框,用于将结束日期设置为开始日期后一天以及我需要考虑的其他范围规则。在根据某些规则更改值时将数据绑定配置到 VM 时,事件会再次触发并最终覆盖我所做的选择。我最终不得不输入 bool 值来帮助了解事件处理程序是否应该继续,然后' s 潜在的竞争条件或不知道事件处理程序(在另一个线程中)是否应该等待。很快就会变得混乱。
所以,从现在开始,我的方法是创建一个大模型,它接触数据库,可以根据捕获的数据进行验证规则检查,但我将创建一个更小、更轻的版本,它只包含数据绑定的属性。与真实模型的分离仍然存在,Presenter / Controller 响应的任何事件都可以在表单数据验证/数据持久性时从 VM 复制到主模型。如果我正在响应事件,那么设置 vm 值绑定到哪个更轻的 VM,我可以创建一个全新的 VM 实例并重新分配验证结果,然后将此新 VM 实例设置为 .DataSource当我准备好时视图上的 BindingSource 可以避免事件处理程序混乱。
主模型可以响应 NotifyPropertyChanged 事件来更新自己的变化,或者更好的是让演示者在正确的时间这样做。
顺便说一句,Visual Studio 2012 和 2013 现在似乎在设计器中处理通用控件,这非常酷。
作为旁注,我最近一直在涉足 iOS 开发。我印象非常深刻的一件事是他们在 MVC 中作为流程的一部分烘焙的方式,与 .NET 不同,它允许我们想出各种黑客方式来做这件事。我从中吸取了一些教训并将它们应用于.NET,发现我的大脑并没有那么严重。我特别喜欢的一件事是列表控件的工作方式,它很像 Qt(C++ 框架)的 MVC 控件。拥有单一后端对象列表但视图仅在可见区域中保存所需内容的能力比 .NET 控件的默认行为要好得多。
无论如何,祝 .NET 数据绑定好运。我个人会向任何新来者推荐......不要使用它,只需让控制器在适当的时间明确分配所有值。但是,如果您感到舒服并理解令人讨厌的细微差别,我希望我所说的某些内容能传达给某人。