WinForms 中的模型-视图-呈现器

发布于 2024-10-14 00:53:08 字数 925 浏览 2 评论 0原文

我第一次尝试使用 WinForms 来实现 MVP 方法。

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

在我的程序中,我有一个 GUI 按钮,单击该按钮会打开一个 openfiledialog 窗口。

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

在 Presenter.openfile() 中,是否应该将该文件的打开委托给模型层,或者由于没有数据或逻辑要处理,是否应该简单地响应请求并打开 openfiledialog 窗口?

更新:我决定提供赏金,因为我觉得我在这方面需要进一步的帮助,并且最好根据我下面的具体要点进行定制,以便我了解背景。

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

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

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

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

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

我认为它们是我主要关心的问题,如果我了解这些流程应该如何进行,我想我会没事的。

I am trying to implement the MVP method for the first time, using WinForms.

I am trying to understand the function of each layer.

In my program I have a GUI button that when clicked upon opens a openfiledialog window.

So using MVP, the GUI handles the button click event and then calls presenter.openfile();

Within presenter.openfile(), should that then delegate the opening of that file to the model layer, or as there is no data or logic to process, should it simply act on the request and open the openfiledialog window?

Update: I have decided to offer a bounty as I feel I need further assistance on this, and preferably tailored to my specific points below, so that I have context.

Okay, after reading up on MVP, I have decided to implement the Passive View. Effectively I will have a bunch of controls on a Winform that will be handled by a Presenter and then the tasks delegated to the Model(s). My specific points are below:

  1. When the winform loads, it has to obtain a treeview. Am I correct in thinking that the view should therefore call a method such as: presenter.gettree(), this in turn will delegate to the model, which will obtain the data for the treeview, create it and configure it, return it to the presenter, which in turn will pass to the view which will then simply assign it to, say, a panel?

  2. Would this be the same for any data control on the Winform, as I also have a datagridview?

  3. My App, has a number of model classes with the same assembly. It also supports a plugin architecture with plugins that need to be loaded at startup. Would the view simply call a presenter method, which in turn would call a method that loads the plugins and display the information in the view? Which tier would then control the plugin references. Would the view hold references to them or the presenter?

  4. Am I correct in thinking that the view should handle every single thing about presentation, from treeview node colour, to datagrid size, etc?

I think that they are my main concerns and if I understand how the flow should be for these I think I will be okay.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无所谓啦 2024-10-21 00:53:08

演示者应按照您的建议对请求端进行操作,显示 openfiledialog 窗口。由于模型不需要任何数据,演示者可以而且应该处理该请求。

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

The presenter should act on the request end show the openfiledialog window as you suggested. Since no data is required from the model the presenter can, and should, handle the request.

Let's assume you need the data to create some entities in your model. You can either pass the stream trough to the access layer where you have a method to create entities from the stream, but I suggest you handle the parsing of the file in your presenter and use a constructor or Create method per entity in your model.

若无相欠,怎会相见 2024-10-21 00:53:08

这是我对 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.Show() 之前从 IConfigurationPresenter.ShowView() 调用 IConfigurationView.SetTreeData(...)代码>

< strong>2. 对于 Winform 上的任何数据控件来说,这是否相同,因为我也有一个 datagridview?

是的,我会为此调用IConfigurationView.SetTableData(...)。由视图来格式化给定的数据。演示者只需遵守视图的契约,即它需要表格数据。

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

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

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

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

点击节点的数据怎么样?

5. 如果当我点击树节点时,我是否应该将特定节点传递给演示者,然后从该节点传递给演示者会计算出它需要什么数据,然后向模型询问该数据,然后将其呈现回视图?

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


This is my humble take on MVP and your specific issues.

First, anything that a user can interact with, or just be shown, is a view. The laws, behavior and characteristics of such a view is described by an interface. That interface can be implemented using a WinForms UI, a console UI, a web UI or even no UI at all (usually when testing a presenter) - the concrete implementation just doesn't matter as long as it obeys the laws of its view interface.

Second, a view is always controlled by a presenter. The laws, behavior and characteristics of such a presenter is also described by an interface. That interface has no interest in the concrete view implementation as long as it obeys the laws of its view interface.

Third, since a presenter controls its view, to minimize dependencies there's really no gain in having the view knowing anything at all about its presenter. There's an agreed contract between the presenter and the view and that's stated by the view interface.

The implications of Third are:

  • The presenter doesn't have any methods that the view can call, but the view has events that the presenter can subscribe to.
  • The presenter knows its view. I prefer to accomplish this with constructor injection on the concrete presenter.
  • The view has no idea what presenter is controlling it; it'll just never be provided any presenter.

For your issue, the above could look like this in somewhat simplified code:

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();
    }
}

In addition to the above, I usually have a base IView interface where I stash the Show() and any owner view or view title that my views usually benefit from.

To your questions:

1. When the winform loads, it has to obtain a treeview. Am I correct in thinking that the view should therefore call a method such as: presenter.gettree(), this in turn will delegate to the model, which will obtain the data for the treeview, create it and configure it, return it to the presenter, which in turn will pass to the view which will then simply assign it to, say, a panel?

I would call IConfigurationView.SetTreeData(...) from IConfigurationPresenter.ShowView(), right before the call to IConfigurationView.Show()

2. Would this be the same for any data control on the Winform, as I also have a datagridview?

Yes, I would call IConfigurationView.SetTableData(...) for that. It's up to the view to format the data given to it. The presenter simply obeys the view's contract that it wants tabular data.

3. My App, has a number of model classes with the same assembly. It also supports a plugin architecture with plugins that need to be loaded at startup. Would the view simply call a presenter method, which in turn would call a method that loads the plugins and display the information in the view? Which tier would then control the plugin references. Would the view hold references to them or the presenter?

If the plugins are view-related, then the views should know about them, but not the presenter. If they are all about data and model, then the view shouldn't have anything to do with them.

4. Am I correct in thinking that the view should handle every single thing about presentation, from treeview node colour, to datagrid size, etc?

Yes. Think about it as the presenter providing XML that describes data and the view that takes the data and applies a CSS stylesheet to it. In concrete terms, the presenter might call IRoadMapView.SetRoadCondition(RoadCondition.Slippery) and the view then renders the road in red color.

What about data for clicked nodes?

5. If when I click on the treenodes, should I pass through the specific node to the presenter and then from that the presenter would work out what data it needs and then asks the model for that data, before presenting it back to the view?

If possible, I would pass all data needed to present the tree in a view in one shot. But if some data is too large to be passed from the beginning or if it's dynamic in its nature and needs the "latest snapshot" from the model (via the presenter), then I would add something like event LoadNodeDetailsEventHandler LoadNodeDetails to the view interface, so that the presenter can subscribe to it, fetch the details of the node in LoadNodeDetailsEventArgs.Node (possibly via its ID of some kind) from the model, so that the view can update its shown node details when the event handler delegate returns. Note that async patterns of this might be needed if fetching the data might be too slow for a good user experience.

写下不归期 2024-10-21 00:53:08

演示者包含视图中的所有逻辑,应以 @JochemKempe 。实际上,按钮单击事件处理程序调用 Presenter.OpenFile()。然后演示者能够确定应该做什么。

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

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

在我看来,使用 MVP 模式的主要原因是将 UI 技术与视图逻辑分开。因此,演示者协调所有逻辑,而视图将其与 UI 逻辑分开。这具有非常好的副作用,使演示者完全可进行单元测试。

更新:由于演示者是在一个特定视图中找到的逻辑的体现,因此视图-演示者关系在我看来是一对一的关系。出于所有实际目的,一个视图实例(例如表单)与一个演示者实例交互,而一个演示者实例仅与一个视图实例交互。

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

The presenter, which contains all logic in the view, should respond to the button being clicked as @JochemKempe says. In practical terms, the button click event handler calls presenter.OpenFile(). The presenter is then able to determine what should be done.

If it decides that the user must select a file, it calls back into the view (via a view interface) and let the view, which contains all UI technicalities, display the OpenFileDialog. This is a very important distinction in that the presenter should not be allowed to perform operations tied to the UI technology in use.

The selected file will then be returned to the presenter which continues its logic. This may involve whatever model or service should handle processing the file.

The primary reason for using an MVP pattern, imo is to separate the UI technology from the view logic. Thus the presenter orchestrates all logic while the view keeps it separated from UI logic. This has the very nice side effect of making the presenter fully unit testable.

Update: since the presenter is the embodiment of the logic found in one specific view, the view-presenter relationship is IMO a one-to-one relationship. And for all practical purposes, one view instance (say a Form) interacts with one presenter instance, and one presenter instance interacts with only one view instance.

That said, in my implementation of MVP with WinForms the presenter always interacts with the view through an interface representing the UI abilities of the view. There is no limitation on what view implements this interface, thus different "widgets" may implement the same view interface and reuse the presenter class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文