网上已经有很多讲MVC、MVP、MVVM模式的区别和原理的文章,这里不细说了。具体可以翻本文的参考资料。这里主要讲讲实际项目中的一些经验。
背景
工作原因接手一个用C#开发的Winforms软件,代码大概一万多行,业务逻辑完全和界面混在一起,没有单元测试。考虑到后面还要优化、修改,每次下手之前读代码都得半天,于是决定重构成MVP模式。
MVP模式又分为Passive View
和Supervising Controller
,为了便于测试,这里选了Passive View
模式,把View和Model彻底隔离。基本架构如下:
- 一个Model,多个Views;
- Views由一个主窗体MainForm和多个子视图UserControl组成;
- 每个View都有一个接口IView,并分别对应一个Presenter;
- Views和Model互相不知道对方,且不引用任何一个Presenter;
- 程序调用
Application.Run(MainForm)
之前初始化所有的Presenter、Views和Model; - Presenter的构造函数中传入对应的IView和Model;
Model的实现
通常Winform
的MVP模式
中,只有一个model
,这样可以保证presenter
和其他弹出式form
的构造函数只需要传入一个model
。如果model
中包含的内容比较多,可以包含其他类作为属性。
如果model
中的属性是类,需要清空该类中的所有属性时,不能采用new
的方法,最好是写一个方法,将所有属性赋初始值。示例代码如下:
// Winform MVP模式中的 model和view结构
namespace My.Models
{
// MVP中的主 model
public class Model
{
public Class1 SubModel1 { get; set; }
public Class2 SubModel2 { get; set; }
}
public class Class1
{
// 初始化 Class1 中的属性值
public void Clear() { }
}
public class Class2
{
// 初始化 Class2 中的属性值
public void Clear() { }
}
}
namespace My.Presenters
{
using Models;
// MVP中的主 presenter
public class Presenter
{
private Model _model;
private Class1 _subModel1;
private Class2 _subModel2;
// 构造函数
public Presenter(Model model)
{
_model = model;
_subModel1 = _model.SubModel1;
_subModel2 = _model.SubModel2;
}
}
}
UserControl和MainForm之间的值传递
UserControl的用法可以参考之前写的文章。
MainForm和UserControl为父子关系,两者都有接口和presenter,有公共属性与各自控件的值绑定。如果不考虑复用,有以下两种情况:
从Form
到UserControl
直接将UserControl拖动添加到Form中,在Form中添加公共属性指向UserControl,就可以直接在Form的Presenter中传值给UserControl。
// Form View
public class Form1
{
private TextBox textBox1;
private UC1 uc1;
//公共属性与textBox控件值绑定
public string TxtValue1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
// UserControl实例可以通过公共属性获取
public UC1 UC1 => uc1;
}
// UserControl View
public class UC1 : UserControl
{
private TextBox textBox2;
public string TxtValue2
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
}
// Form Presenter
public class MainPresenter
{
private Form1 _view;
public MainPresenter(Form1 view)
{
_view = view;
}
public void Method1()
{
// 直接通过公共属性访问
_view.UC1.TxtValue2 = _view.TxtValue1;
}
}
从UserControl
到Form
在UserControl中使用parent属性可以操作Form的公共属性。
// UserControl View
public string ID2
{
get { return textBox1.Text; }
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
var textBoxContent = this.textBox1.Text;
//这样UserControl与特定的Form1产生依赖,复用UC时需要更改代码
var parent = this.Parent as Form1;
parent.ID2 = ID2;
}
// Form View
public string ID2
{
set { textBox1.Text = value; }
}
更优雅的实现方式,使用事件委托
参考:Transferring information between two forms
参考资料:
[1] 界面之下:还原真实的 MVC、MVP、MVVM 模式
[2] MVP Design Pattern for C# Window Form?
[3] Implement MVP for main form and sub-views
[4] Winforms MVP Pattern w/ Multiple Views
[5] Introducing MVP (Model-View-Presenter) Pattern (WinForms)