WinForms中的Model-View-Presenter

时间:2011-01-25 13:35:43

标签: c# .net winforms design-patterns mvp

我正在尝试使用WinForms首次实现MVP方法。

我试图了解每一层的功能。

在我的程序中,我有一个GUI按钮,当点击它时会打开一个openfiledialog窗口。

因此,使用MVP,GUI处理按钮单击事件,然后调用presenter.openfile();

在presenter.openfile()中,是否应该将该文件的开头委托给模型层,或者因为没有要处理的数据或逻辑,它应该只是对请求进行操作并打开openfiledialog窗口吗?

更新:我决定提供赏金,因为我觉得我需要进一步的帮助,最好根据我的具体要点量身定制,以便我有上下文。

好的,在阅读了MVP之后,我决定实施被动视图。实际上,我将在Winform上有一堆控件,由Presenter处理,然后委托给Model。我的具体要点如下:

  1. 当winform加载时,必须获取树视图。我认为视图应该调用一个方法如:presenter.gettree(),这是正确的,这将依次委托给模型,该模型将获取树视图的数据,创建并配置它,将其返回给演示者,然后将传递到视图,然后将其简单地分配给一个面板?

  2. 对于Winform上的任何数据控件,这是否相同,因为我还有一个datagridview?

  3. 我的应用程序,有许多具有相同程序集的模型类。它还支持插件架构,其插件需要在启动时加载。视图是否只是调用一个presenter方法,而这个方法又会调用加载插件并在视图中显示信息的方法?然后哪个层控制插件引用。视图是否会引用它们或演示者?

  4. 我认为视图应该处理有关演示的所有内容,从树视图节点颜色到数据网格大小等,我是否正确?

  5. 我认为这是我的主要关注点,如果我理解这些流程应该如何,我想我会没事的。

3 个答案:

答案 0 :(得分:117)

这是我对MVP和你的具体问题的谦虚看法。

首先,用户可以与之交互或只是显示的任何内容都是视图。这种视图的规律,行为和特征由接口描述。该接口可以使用WinForms UI,控制台UI,Web UI甚至根本没有UI(通常在测试演示者时)实现 - 只要遵循其视图界面的规律,具体实现无关紧要

第二次,视图始终由演示者控制。此类演示者的法律,行为和特征也由接口描述。只要符合其视图界面的规律,该接口就不具体对具体视图实现感兴趣。

第三,因为演示者控制其视图,为了最大限度地减少依赖关系,让视图知道关于其演示者的任何内容实际上没有任何好处。演示者和视图之间有一个约定的合同,并且由视图界面说明。

第三的含义是:

  • 演示者没有视图可以调用的任何方法,但视图包含演示者可以订阅的事件。
  • 主持人知道它的观点。我更喜欢在混凝土演示器上使用构造函数注入来完成此任务。
  • 视图不知道演示者控制它的是什么;它永远不会被任何主持人提供。

对于您的问题,上面的代码看起来有点简化:

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

除了上述内容之外,我通常还有一个基本的IView界面,用于存放Show()以及我的观点通常会从中受益的所有者视图或视图标题。

问题:

1。 当winform加载时,它必须获取树视图。我认为视图应该调用一个方法如:presenter.gettree(),这是正确的,这将依次委托给模型,该模型将获取树视图的数据,创建并配置它,将其返回给演示者,然后将传递到视图,然后将其简单地分配给一个面板?

  

我会在致电IConfigurationView.SetTreeData(...)之前致电IConfigurationPresenter.ShowView() IConfigurationView.Show()

2. 对于Winform上的任何数据控件,这是否相同,因为我还有一个datagridview?

  

是的,我会打电话给IConfigurationView.SetTableData(...)。这取决于格式化给它的数据。演示者只需遵守视图的合同,即它需要表格数据。

3. 我的应用程序,有许多具有相同程序集的模型类。它还支持插件架构,其插件需要在启动时加载。视图是否只是调用一个presenter方法,而这个方法又会调用加载插件并在视图中显示信息的方法?然后哪个层控制插件引用。视图是否会引用它们或演示者?

  

如果插件与视图相关,那么视图应该知道它们,而不是演示者。如果它们都与数据和模型有关,那么视图就不应该与它们有任何关系。

4. 我认为视图应该处理有关演示的所有内容,从树视图节点颜色到数据网格大小等,我是否正确?

  

是。将其视为提供描述数据的XML的演示者以及获取数据并将CSS样式表应用于其的视图。具体而言,演示者可能会调用IRoadMapView.SetRoadCondition(RoadCondition.Slippery),然后视图会将道路呈现为红色。

点击节点的数据怎么样?

5. 如果我点击treenodes,我是否应该通过特定节点到达演示者,然后从演示者那里找出所需的数据,然后询问该数据的模型,然后再将其呈现给视图?

  

如果可能的话,我会一次性传递在视图中呈现树所需的所有数据。但是如果某些数据太大而无法从头开始传递,或者它本质上是动态的并且需要来自模型的“最新快照”(通过演示者),那么我会在视图中添加类似event LoadNodeDetailsEventHandler LoadNodeDetails的内容接口,以便演示者可以订阅它,从模型中获取LoadNodeDetailsEventArgs.Node中的节点的详细信息(可能通过其某种ID),以便视图可以在事件处理程序时更新其显示的节点详细信息代表回报。请注意,如果获取数据的速度可能太慢而无法获得良好的用户体验,则可能需要使用异步模式。

答案 1 :(得分:10)

包含视图中所有逻辑的演示者应该响应被点击的按钮@JochemKempe says。实际上,按钮单击事件处理程序调用{​​{1}}。然后,演示者能够确定应该做什么。

如果它确定用户必须选择一个文件,将回调到视图(通过视图界面),并让包含所有UI技术细节的视图显示{{1} }。这是一个非常重要的区别,因为不应允许演示者执行与使用中的UI技术相关的操作。

然后,所选文件将返回到演示者,继续其逻辑。这可能涉及处理文件的任何模型或服务。

使用MVP模式的主要原因是imo将UI技术与视图逻辑分开。因此,当视图使其与UI逻辑分离时,演示者协调所有逻辑。这具有非常好的副作用,使演示者完全可以单元测试。

更新,因为演示者是在一个特定视图中找到的逻辑的体现,所以视图 - 演示者关系是IMO的一对一关系。并且出于所有实际目的,一个视图实例(例如表单)与一个演示者实例交互,并且一个演示者实例仅与一个视图实例交互。

也就是说,在我使用WinForms实现MVP时,演示者总是通过表示视图UI能力的界面与视图交互。对于什么视图实现此接口没有限制,因此不同的“小部件”可以实现相同的视图接口并重用演示者类。

答案 2 :(得分:2)

演示者应该按照您的建议在请求结束时显示openfiledialog窗口。由于模型不需要数据,因此演示者可以并且应该处理请求。

假设您需要数据来在模型中创建一些实体。您可以将流传递到访问层,在那里您可以使用从流中创建实体的方法,但我建议您在演示者中处理文件的解析,并在模型中使用构造函数或每个实体的Create方法。 / p>