三者之间的关系

在MVP初探里简单的描述了V和P之间是如何交互的。

无论是PV还是SC,M\V\P这三者之间的关系并没有发生改变,V只是前端的客户代理承现展显数据,P是如何处理客户交互行为的决策者。

数据是P主动“推”给V的,而V只向P发送用户通知,都是单向的;所以在IView中被Presenter调用的方法应该都是没有返回值的。可以通过调用IView中的方法,也可以通过事件注册的方式来实现P/V之间的交互。

那么,在PV模式下View是被动的,而P则控制着对V请求的处理;view仅将用户的(事件)请求提交到P,它并不参与对用户请求的处理,如果在这个过程中P要V参与(如:显示实时的数据等)也是P将要展现的数据PUSH到V,对于处理的结果的呈现也是同样的处理方式。

而P在此过程中的角色是一个决策者,它决定如何处理V提交过来的用户请求,如何去实现业务逻辑和调用Model。

如何维护三者的关系

View仅是传递用户请求和对数据的呈现;Presenter是事件的的决策者;Model业务逻辑执行者。

在PV中三者的关系使用的uView可以直接调用Presenter,这样就会使用的在开发过程中将Presenter作为View和Model之间的一种代理(Proxy)而不是Presenter.

那么我们就可以通过事件订阅机制来解决这个问题。

编程模型的实现

首先,要让View不能访问到Presenter,我们可以通过事件订阅机制;为所有的Presenter创建基类Presenter<IView>,泛型类型IView表示具体View实现的接口。表示View的同名只读属性在构造函数中赋值,赋值完成之后调用调用虚方法OnViewSet,使得对具体的Presenter可以重写该方法进行对View进行事件注册工作。

1 namespace Handwe.Demo.MVP
 2 {
 3    public class Presenter<IView>
 4     {
 5        public IView View { get; set; }  
 6 
 7        public Presenter(IView view){
 8            this.View = view;
 9            this.OnViewSet();
10        }
11 
12        protected virtual void OnViewSet()
13        {
14 
15        }
16     }
17 }

 其次,Presenter是通过接口的方式与View进行交互的。有时候我们要通过这个接口访问Form的一些属性、方法和事件,需要将相应的成员定义在接口上面,可能会有很多,添加的删减等。那么,我们可以将这些成员定义在一个接口中,具体View的接口继承该接口就可以了。在这里,我们相当是为所有的View接口创建了“基接口”。如下:

1 namespace Handwe.Demo.MVP
 2 {
 3     public interface IViewBase
 4     {
 5         event EventHandler Load;
 6         event EventHandler Closed;
 7         event CancelEventHandler Closing;
 8 
 9         string ResultText
10         {
11             get;set;
12         }
13 
14     }
15 }

同时,也要创建所有的View创建基类ViewBase,做必要的初始化工作,如加载基础数据等:

1 namespace Handwe.Demo.MVP
 2 {
 3     public class ViewBase : Form
 4     {
 5         public ViewBase()
 6         {
 7             this.CreatePresenter();
 8         }
 9 
10         protected virtual object CreatePresenter()
11         {
12             if (LicenseManager.CurrentContext.UsageMode == LicenseUsageMode.Designtime)
13             {
14                 return null;
15             }
16             else
17             {
18                 throw new NotImplementedException(string.Format("{0} must override the CreatedPresenter method.", this.GetType().FullName));
19             }
20         }
21     }
22 }

 在ViewBase中调用CreatedPresenter(),它的实现在具体的每个View中也就是实现Presenter和View对应;

1 public partial class CalculatorView : ViewBase, ICalculatorView
2 ...
3 protected override object CreatePresenter()
4         {
5             return new CalculatePresenter(this);
6         }
7
8 ...
9 }

实例的实现:

vgpu p4 切换工作模式_vgpu p4 切换工作模式

那边现在可以定义View的接口ICalculatorView,在这里定义了一个事件Calculating,也就是点击btnCalculate触发,还要定义三个方法,DisplayResult(int result)、DisplayMsg(stirng msg)、还有一个是Clear();具体什么用途看名称就知道了,如下:

1 namespace Handwe.Demo.UnityInMVP
 2 {
 3     public interface ICalculatorView : IViewBase
 4     {
 5         event EventHandler<CalculateEventArgs> Calculating;
 6 
 7 
 8         void DisplayResult(int result);
 9 
10         void DisplayMsg(string msg);
11 
12         void Clear();
13     }
14 }

这里有一个CalculateEventArgs,它是自定义的事件参数类型:

1 namespace Handwe.Demo.UnityInMVP
2 {
3     public class CalculateEventArgs : EventArgs
4     {
5         public string OperationNum1 { get; set; }
6 
7         public string OperationNum2 { get; set; }
8     }
9 }

很简单只定义了两个属性;

 接着来实现CalculatorView,代码如下:

1 namespace Handwe.Demo.UnityInMVP
 2 {
 3     public partial class CalculatorView : ViewBase, ICalculatorView
 4     {
 5         public CalculatorView()
 6         {
 7             InitializeComponent();
 8         }
 9         
10         public string ResultText
11         {
12             get { return this.labResult.Text; } set{this.labResult.Text = value;}
13         }
14 
15         protected override object CreatePresenter()
16         {
17             return new CalculatorPresenter();
18         }
19 
20         #region ICalculateView Members
21         public event EventHandler<CalculateEventArgs> Calculating;
22         
23         public void DisplayResult(int result)
24         {
25             this.textBoxResult.Text = result.ToString();
26         }
27 
28         public void Clear()
29         {
30             this.textBoxOp1.Text = string.Empty;
31             this.textBoxOp2.Text = string.Empty;
32 
33             this.DisplayMsg(string.Empty);
34         }
35 
36         public void DisplayMsg(string msg)
37         {
38             this.labDisplayMsg.Text = msg;
39         }
40         #endregion
41 
42         private void btnCalculate_Click(object sender, EventArgs e)
43         {
44             string op1 = this.textBoxOp1.Text.Trim();
45             string op2 = this.textBoxOp2.Text.Trim();
46             this.OnCalculating(op1, op2);
47         }
48 
49         private void btnClear_Click(object sender, EventArgs e)
50         {
51         }
52 
53         private void btnCheck_Click(object sender, EventArgs e)
54         {
55         }
56 
57         protected virtual void OnCalculating(string op1, string op2)
58         {
59             if (null != this.Calculating)
60             {
61                 this.Calculating(this, new CalculateEventArgs { OperationNum1 = op1, OperationNum2 = op2 });
62             }
63         }
64     }
65 }

然后我们实现事件的订阅者,这里就是CalculatePresenter:

1 namespace Handwe.Demo.UnityInMVP
 2 {
 3     public class CalculatePresenter : Presenter<ICalculatorView>
 4     {
 5         public CalculatePresenter(ICalculatorView view):base(view)
 6         {
 7             this.Calculate = new Calculate();
 8         }
 9
10
11         public Calculate Calculate
12         {
13             get;
14             set;
15         }
16 
17         protected override void OnViewSet()
18         {
19             this.View.Load += (sender, args) =>
20                 {
21 
22                     this.View.Clear();
23                 };
24 
25             this.View.Calculating += (sender, args) =>
26             {
27                 int op1, op2;
28                 if (!int.TryParse(args.OperationNum1, out op1))
29                 {
30                     this.View.DisplayMsg(string.Format("{0} type invalid.", args.OperationNum1));
31                     return;
32                 }
33 
34                 if (!int.TryParse(args.OperationNum2, out op2))
35                 {
36                     this.View.DisplayMsg(string.Format("{0} type invalid.", args.OperationNum1));
37                     return;
38                 }
39                 int result = this.Calculator.Divide(op1, op2);
40 
41                 this.View.DisplayResult(result);
42 
43                 this.View.Clear();
44                 this.View.DisplayMsg("please type operation numbers.");
45             };
46         }
47     }
48 }