如何在MVP被动视图中显示复杂数据

时间:2014-09-10 16:18:52

标签: c# winforms design-patterns mvp

我一直在寻找MVP模式一段时间,并设法创建一些简单的MVP兼容应用程序。

我现在正在尝试将模式应用于更复杂的应用程序,我对这样做的最佳方式有一些疑问。

我的应用程序有一个WinForm,有两个按钮用于加载两种不同类型的数据。我的视图界面如下所示:

interface IView_MainForm
{
    //  Load input
    //
    event EventHandler<InputLoadEventArgs> LoadInput_01;
    event EventHandler<InputLoadEventArgs> LoadInput_02;

    bool Input01_Loaded { get; set; }
    bool Input02_Loaded { get; set; }
}

IView通过构造函数注入在我的演示者中引用:

public Presenter_MainForm(IView_MainForm view)
{
    this.View = view;
    this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
    this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}

到目前为止,这么好。当用户单击两个按钮中的任何一个来加载数据时,会引发一个LoadInput _ ##事件,Presenter正在处理它,检查输入是否有错误并根据我的数据模型对其进行构造。

我的下一步是在View中显示已处理的数据。

我正努力让我的视图尽可能被动和“愚蠢”,假设它对Presenter一无所知(它没有订阅它的事件,Presenter通过调用IView方法将数据发送到View) ,更不用说模特了。

如果View不知道数据模型是什么样的,我应该如何填充像TreeView这样的控件呢?

另外,我是否将整个MVP视为正确,或者我有什么遗漏?

1 个答案:

答案 0 :(得分:1)

complex type中拥有View属性没有任何问题。假设您有一些ComplexType

class ComplexType
{
   public string ParentNode {get;set;}
   public List<string> ChildNodes {get;set;}
   // some other properties
}

我们还假设ComplexType是您TreeView的数据模型。使用MVP模式完全没有问题可以使View上的属性具有ComplexType。所以有这样的东西是完全没问题的

interface IView_MainForm
{
    //  Load input
    //
    event EventHandler<InputLoadEventArgs> LoadInput_01;
    event EventHandler<InputLoadEventArgs> LoadInput_02;

    bool Input01_Loaded { get; set; }
    bool Input02_Loaded { get; set; }

    ComplexType Input01Data {get;set;} // you might actually have some code in get/set setters
    ComplexType Input02Data {get;set;} // you might actually have some code in get/set setters

    public void SetInput01Data(ComplexType input01Data)
    {
       Input01Data = input01Data;
       // some other stuff
    }
}

由于您的Model适用于View,其中包含2个输入,因此您的模型可能看起来像这样

public interface IModel
{
   public ComplexType Input01Data {get;set;}
   public ComplexType Input02Data {get;set;}
}

现在,在您的Presenter中,您只需处理从View触发的事件,填充Model并在View上设置属性

class Presenter
{
    private IModel _myModel...
    private IRepository _repository;
    public Presenter(IView_MainForm view, IRepository repository)
    {
        _repository = repository;
        this.View = view;
        this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
        this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
    }

    public void OnLoadInput_01(object sender, InputLoadEventArgs e)
    {
         // get data based on passed arguments (e.SomeProperty)
         // construct IModel
         myModel = _repository.GetData(e.SomeProperty);
         // pass data to IView_MainForm
         View.SetInput01Data(myModel.Input01Data);
     }
}

关于你的关注

  

我努力让自己的观点尽可能地被动和“愚蠢”,   假设它对Presenter一无所知(它没有订阅   它的事件,Presenter通过调用IView将数据发送到View   而不是模型。

您的View仍然不了解PresenterModel。它只是触发事件,从Presenter获取数据并绑定其控件。你有testability(请注意这个单元测试是伪代码,因为我不知道你如何检索数据,你在按钮点击事件中需要什么输入......)。

[Test]
public void ShouldLoadInput01DataOnButtonClick()
{
   // Arrange
   IModel data = // create dummy data

   Mock<IView_MainForm> clientsViewMock = new  Mock<IView_MainForm>();
   Mock<IRepository> clientsRepositoryMock = new Mock<IRepository>();

   clientsRepositoryMock.Setup(repository => repository.GetData(something)).Returns(data.Input01Data);     
   var presenter = new Presenter(clientsViewMock.Object, clientsRepositoryMock .Object);

   // Act
   clientsViewMock.Raise(view => view.LoadInput01 += null, new InputLoadEventArgs());

   // Assert
   clientsViewMock.Verify(view => view.SetInput01Data(data.Input01Data), "Input01 data expected be set on button click.");
}