【问题标题】:How to display complex data in MVP Passive View如何在 MVP Passive View 中显示复杂数据
【发布时间】:2014-09-10 16:18:52
【问题描述】:

我一直在研究 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) ,更不用说模型了。

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

另外,我是否正确地了解了整个 MVP 的内容,还是我遗漏了什么?

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 你的视图(Form)是否实现了IView_MainForm
  • 如果你的view 不知道data model 是什么(我假设你的意思是Model),它应该如何将自己绑定到数据?
  • @JohnSaunders,明白了,谢谢。 @huMptyduMpty,是的,我的观点(MainForm)实现了IView_MainForm,这就是我现在能够在基本级别实现MVP模式的方式:Presenter在其构造函数中将IView_MainForm作为参数,通过调用接口中指定的方法(由MainForm实现)订阅其事件并显示结果。
  • @michaelmoore 这正是问题所在。据我了解,在 MVP(PV) 中,ViewModel 彼此一无所知,因为它是 Presenter 处理用户事件并指示 View 如何显示数据。如果我的View 已经知道我的数据是什么样的,那么首先MVP 的意义何在?然后我可以通过直接引用我的Model 来简单地填充我的控件...我在这里遗漏了什么吗?

标签: c# winforms design-patterns mvp


【解决方案1】:

在您的View 中拥有complex type 属性并没有错。假设你有一些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 用于具有 2 个输入的 View,因此您的模型可能看起来像这样

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 通过调用 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.");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-27
    • 2018-08-03
    • 2011-11-29
    • 1970-01-01
    • 2013-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多