• c#中winform的MVP模式的简单实现


    MVP模式是类似于MVC模式的一种设计模式,最近在做项目学习过程中遇到,弄了很久终于有一些眉目,这是学习过程中的一些笔记。
    MVP指的是实体对象Model、视图Viw和业务处理Presenter。MVP的作用是解耦UI渲染、业务逻辑和数据实体的关系。在普通的winform中,业务和界面是写在一起的,一般都是同一个Load或Click方法中,使View和Controller紧密联系。在MVP中,我们将界面渲染放在View里面,也就是winfrom的窗体应用类;将业务关系放在Presenter类中,这就是MVP中的业务数据类;最后的数据实体与数据库的交互放在Model中,是三者各司其职。
    一般的MVP中,我们是在Presenter中主动使用View,也就是界面控件形态都是由Presenter去主动控制的。而建立这两者之间的联系,就是在Presenter中注册View的事件,当界面发生由用户触发事件时,这个事件会通过View传递到Presenter中,并在Presenter中调用Model的数据方法,最后Presenter调用在类中引用的View的实例去改变界面形态,下面是一些方法的实现,这里着重说明Presenter和View的关系。
    首先,我们定义View的接口类IView,里面就一个数据实体的引用:

    1 public interface IView<T>:IView
    2 {
    3     T Model { get; set; }
    4 }
    public interface IView:IView

    接下来,定义View的下一级接口,这里是定义视图一些控件和事件:

    1 public interface IMainForm<T> : IView<T>
    2 {
    3     Button TestButton { get;}    //定义MainFrom的按钮引用
    4     TextBox TestTextBox { get; }    //定义MianForm的文本框引用
    5     event EventHandler ViewLoadEvent;    //定义窗体加载完毕执行事件
    6     event EventHandler ButtonSubmitEvent;    //定义按钮事件
    7     void ShowSubmitDialog();    //定义自定义的事件
    8 }
    public interface IMainForm : IView

    最后就是View的实现类,里面是实现的接口方法和属性,包含一个按钮及一个文本框,这里有一个继承了的MvpForm类和PresenterBinding的特性,一会再说:

     1 [PresenterBinding(typeof(MainFormPresenter))]
     2 public partial class MainForm : MvpForm , IMainForm<MainFormModel>
     3 {
     4     public MainForm()
     5     {
     6         InitializeComponent();
     7     }
     8 
     9     public MainFormModel Model { get; set; }
    10     public TextBox TestTextBox { get { return txtText; } }
    11     public Button TestButton { get { return btnSubmit; } }
    12 
    13     public event EventHandler ViewLoadEvent;
    14     public event EventHandler ButtonSubmitEvent;
    15 
    16     private void MainForm_Load(object sender, EventArgs e)
    17     {
    18         if (ViewLoadEvent != null) ViewLoadEvent(sender, e);
    19     }
    20 
    21     public void ShowSubmitDialog()
    22     {
    23         MessageBox.Show("to submit?");
    24     }
    25 
    26     private void btnSubmit_Click(object sender, EventArgs e)
    27     {
    28         if (ButtonSubmitEvent != null) ButtonSubmitEvent(sender , e);
    29     }
    30 }
    public partial class MainForm : MvpForm , IMainForm

    定义完View的内容,就可以看一下Presenter,同样,先有接口,再有实现,先定义Presenter的接口:

    1 public interface IPresenter<T>:IPresenter where T : class, View.IView
    2 {
    3     T View { get; }
    4 }
    public interface IPresenter:IPresenter where T : class, View.IView

    这里再定义一个Presenter的抽象类,用于统一各个不同View对应的Presenter类,其定义如下:

     1 public abstract class Presenter<T> : IPresenter<T> where T : class, View.IView
     2 {
     3     private readonly T view;
     4 
     5     //这里的view作为引用,用于在presenter中获取View的实例
     6     protected Presenter(T view)
     7     {
     8     this.view = view;
     9     }
    10 
    11     public T View { get { return view; } }
    12 }
    public abstract class Presenter : IPresenter where T : class, View.IView

    最后就是对应的View的Presenter类了:

     1 class MainFormPresenter:Presenter<View.IMainForm<Model.MainFormModel>>
     2 {
     3     public MainFormPresenter(View.IMainForm<Model.MainFormModel> view)
     4 : base(view)
     5     {
     6     view.Model = new Model.MainFormModel();
     7 
     8     view.ViewLoadEvent += On_ViewLoad;
     9     view.ButtonSubmitEvent += On_ButtonSubmitClick;
    10     init();
    11     }
    12 
    13     public void init()
    14     {
    15         //To Do something...
    16     }
    17 
    18     public void On_ViewLoad(object sender, EventArgs e)
    19     {
    20         //To Do something...
    21     }
    22 
    23     public void On_ButtonSubmitClick(object sender, EventArgs e)
    24     {
    25         View.ShowSubmitDialog();//通过view的实例调用view的方法来改变控件形态
    26     }
    27 }
    class MainFormPresenter:Presenter<View.IMainForm>

    这里定义了Presenter和View的接口和实现,下面就是如何将这两个不同的模块联合在一起,这里,使用的是.net的特性和反射。
    首先,先建立特性类PresenterBindingAttribute,通过在View的实现类标记该特性类和指定对应的Presenter类,就可以获取View和Presenter的对应关系了,该类同样要标注特性AttributeUsage和继承Attribute类,同时定义两个属性参数:

     1 [AttributeUsage(AttributeTargets.Class,AllowMultiple = true)]
     2 public sealed class PresenterBindingAttribute : Attribute
     3 {
     4     public Type PresenterType { get;private set; }
     5 
     6     public Type ViewType { get; set; }
     7 
     8     public PresenterBindingAttribute(Type presenterType)
     9     {
    10         PresenterType = presenterType;
    11         ViewType = null;
    12     }
    13 }
    public sealed class PresenterBindingAttribute : Attribute

    接下来,就是通过反射去建立view和presenter的关系,这里建立PerformBinding类:

     1 public IPresenter PerformBinding(IView viewInstance)
     2 {
     3     IPresenter presenter = null;
     4     Type t = viewInstance.GetType();    //获取该视图的类类型
     5     object[] attrs = t.GetCustomAttributes(typeof(PresenterBindingAttribute), false);    //获取该类上的附加特性集合
     6     //遍历特性集合,找到Presenter类型附加的特性,通过该特性建立实例
     7     foreach (PresenterBindingAttribute pba in attrs)
     8     {
     9         Type newt = pba.PresenterType;    //获取Presenter类类型
    10         //建立Presenter实例,这里的构造参数是View的对象,这样就使两者建立了联系
    11         Object obj = Activator.CreateInstance(pba.PresenterType, viewInstance);
    12         presenter = obj as IPresenter;
    13     }
    14     return presenter;
    15 }
    public IPresenter PerformBinding(IView viewInstance)

    那么,这个类PerformBinding在哪里使用,一般是在应用View启动是就要注册实例,这里为了解除类间的强耦合,就添加一个中间类。在前面的View的实现类中,是继承自一个MvpForm的类,这个MvpForm就使注册View和Presenter关系的类,接下来看MvpForm的实现:

    1 public partial class MvpForm : Form,IView
    2 {
    3     private readonly PresenterBinder presenterBinder = new PresenterBinder();
    4     public MvpForm()
    5     {
    6         presenterBinder.PerformBinding(this);    //注册关系
    7     }
    8 }
    public partial class MvpForm : Form,IView

    这样,就建立了View与Presenter之间的关系,每次View页面启动,就先执行父类MvpForm的构造函数,注册View和Presenter的关系,相应的逻辑可以写在Presenter中,View的作用就是作为UI的渲染。以后添加View和Presenter实现类时,只需要继承和实现相应的类和接口,并在View实现类添加相应的对应类特性,就可实现MVP的设计关系。

  • 相关阅读:
    单位rem 触屏适配总结
    汉字转拼音插件:LM-PinYin.js
    PngOptimizer PNG压缩工具
    H5微信场景应用 audio模块
    LM_ReadImgMode.js PC单页轮播读图模式组件,零依赖!
    js中的eval方法
    IE6利用iframe遮挡 弹层 select
    H5 video安卓默认点击不能全屏播放解决办法
    JS正则截取两个字符串之间的字符串
    定宽320 缩放适配手机屏幕
  • 原文地址:https://www.cnblogs.com/supheart/p/4229554.html
Copyright © 2020-2023  润新知