在 MVP 设计模式中,WinForms 之间的通信

9
我正在开发一个应用程序,遇到了一个大问题。这是一个基于Winforms的C#应用程序,实现了模型-视图-控制器模式,但我对这种方法还不熟悉。我已经搜索了所有地方,但没有找到我的问题的答案。
我需要知道如何使用此模式允许winforms之间的通信,以及Presenter如何显示它们而不将Presenter与表单耦合。我看到一种使用工厂模式的方法,但不知道如何实现它。
任何帮助或指向正确方向的指导都将不胜感激。

你能提供一个你目前尝试实现这个的例子吗?你能提供一个使用工厂模式的技术链接,你不理解吗? - jeuton
4个回答

3

断言

如果遵循被动视图的实现,Presenter负责协调View和Model之间的关系。

可能看起来像这样:

一个View实例化Presenter并将自身注入Presenter:

IPresenter presenter;
public View() { presenter = new Presenter(this) }

一个Presenter实例化一个或多个视图,并将自身注入到视图中:
IView1 view1;
public Presenter() { view1 = new View1(this) } 

IView1 view1;
IView2 view2;
public Presenter() { view1 = new View1(this); view2 = new View2(this); }

例子

在你的情况下,协调多个视图的Presenter可能看起来像这样(伪代码):

public class Presenter : IPresenter
{
  IView1 view1;
  IView2 view2;
  public Presenter() 
  {
    view1 = new View1(this);
    view2 = new View2(this);
  }

  private WireViewEvents()
  {
    view1.OnButtonClick += HandleButtonClickFromView1;
  }

  public void HandleButtonClickFromView1()
  {
    view2.SetSomeData();
    view2.Show();
}

在这个例子中,由View1引发的事件由Presenter处理,数据被设置在View2中,并显示View2
请记住,无论您的实现如何,MVP的目标都是:
  • 关注点分离(UI与领域逻辑分离)。
  • 增加可测试性。
请注意,这只是一个Presenter如何协调多个视图的基本示例。如果您想将View创建与Presenter抽象出来,您可以将创建移动到另一个容器中,Presenter通过该容器调用以创建视图并订阅其事件。

我们真的需要IPresenter吗?我不认为这对Presenter是一个好主意。你能解释一下为什么我们在MVP中需要IPresenter吗? - Sandy
在我的例子中,IPresenter接口并不直接相关。话虽如此,如果“View”是将自己注入Presenter(第一个代码片段),那么这就有很多意义了。通过使Presenter实现一个接口,您允许View将自己连接到任何实现该接口的类。 - jeuton
例如,您的视图可能调用工厂方法来获取正确的Presenter(它不像上面的示例中在构造函数中直接实例化presenter)。您可能有一个AdminUserPresenter:IPresenter,一个RegUserPresenter:IPresenter和一个GuestUserPresenter:IPresenter。视图不知道它正在与哪个Presenter交谈(因为它是从工厂获取Presenter)。视图只知道它正在与IPresenter交谈。 - jeuton
我猜,如果你的“View”服务于多个目的,那么IPresenter就很有用了。我的理解是正确的吗? - Sandy
如果View只有一个Presenter,那么它也是有用的,因为你可以将对Presenter的具体依赖从View中抽象出来(View与接口松耦合而不是与具体类型紧耦合)。但你说得没错,这并非“必要”的。 - jeuton
显示剩余2条评论

3

1

我只是展示了一个虚拟代码,其中两个视图通过Presenter使用接口相互通信。这是一个简单的例子,如果有什么问题,请告诉我。老实说,我没有测试过这段代码。

namespace WindowsFormsApplication1
{
    internal class View1 : IView1
    {
        public View1()
        {
            new Presenter(this);
        }


        public string Username { get; set; }
        public event EventHandler ShowDetails;
    }
}

namespace WindowsFormsApplication1
{
    internal class View2 : IView2
    {
        public View2()
        {
            new Presenter(this);
        }

        public string Position { get; set; }
    }
}

namespace WindowsFormsApplication1
{
    public class Presenter
    {
        private readonly IView1 _view1;
        private readonly IView2 _view2;

        public Presenter(IView1 view1)
        {
            _view1 = view1;
            _view1.ShowDetails += ShowDetails;
        }

        private void ShowDetails(object sender, EventArgs e)
        {
            _view2.Position = _view1.Username == "My Name" ? "Arhchitect" : "Project Manager";
        }

        public Presenter(IView2 view2)
        {
            _view2 = view2;
        }

    }

}

public interface IView1
{
    string Username { get; set; }

    event EventHandler ShowDetails;
}

public interface IView2
{
    string Position { get; set; }
}

但是在这个例子之后,有一些注意事项。首先要决定你的应用程序是要使用1个View接口还是2个。如果你能使用单个接口,则可能会有机会甚至更容易。


0

我认为前面对于模型触发事件以使得Presenter意识到变化的观点是正确的。但我有一些希望能够有用的评论。

首先,View实现可能不是一个单一窗体。有时,通过一个独立的(可能是模态的)窗体来维护模型的一部分,这个窗体实际上像View中的复杂控件一样,这种情况下,窗体之间的交互将是直接的。Presenter不应关心View实现的方式。

其次,当需要交互的表单不明显属于同一个视图时(例如,查找表单),我建议使用应用程序控制器模式。在这种情况下,当表单A需要执行功能(例如“查找产品”或“编辑详细信息”)时,它将调用自己 Presenter 上的方法来执行该操作。Presenter 然后调用应用程序控制器上的单独方法(所有 Presenter 都引用它,它是一个单例),并打开所需的具有其自己 Presenter 的表单。在 WinForms 中,这一切都可以使用模态表单完成,在这种情况下,结果通过调用链发送回来。或者,Application Controller 和 Presenters 之间需要一些事件引发 - 例如,Presenter 在 Application Controller 上引发关于其已完成的内容的事件,并订阅该事件的其他 Presenter 因此受到通知。
有关 MVP 中应用程序控制器模式的更多信息,请参见我的博客文章 使用MVP模式

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接