C#WinForms模型 - 视图 - 演示者(被动视图)

Dan*_*and 20 c# mvp design-patterns passive-view

我正在用C#开发一个WinForms应用程序.我在GUI编程方面经验有限,而且我必须在飞行中学到很多东西.话虽这么说,这就是我正在建设的.

请参阅常规GUI查看以下链接:

GUI http://img227.imageshack.us/img227/1084/program0.jpg

现在,我已经完成了很多工作,但是在非常糟糕的自治设计模式中.我不知道该项目是否会达到一定的规模,因此,是时候进行一些重大的重构了.

我一直在研究GUI设计模式,我希望实现的模式是被动视图(参见http://martinfowler.com/eaaDev/PassiveScreen.html).我正在寻找一些如何将这些结合在一起的帮助.

背景:

1)根据用户在"TreeView"中单击的内容,左下角的"列表"将显示可填充"编辑器"区域的对象列表.这些对象可能是TextBox或DataGridView.用户切换列表以选择他/她想要在"编辑器"中看到的内容

2)该模型本质上是一个包含数据和配置文件的文件夹.有一个外部程序在给定目录上运行,创建输出文件/文件夹等.我正在开发的这个程序旨在以用户友好的方式有效地管理/配置这些对象

3)我一直在做的事情的问题是它几乎不可能测试,因此转向MVP-esque被动视图设计模式

我试图让它成为独立于View的程序.我无法找到任何更复杂的交互式视图与被动视图模式一起使用的示例.

问题:

1)我是否需要为程序的整个"外观"实现一个大的接口/视图,然后为每个TreeView,Editor,Logger等实现子接口/子视图?或者这样做有更好的"结构"吗?

2)当从视图"移交"事件到演示者/控制器时(无论你想使用什么术语WRT被动视图设计模式),我应该这样做的方式是什么?有时我有简单的属性需要更新,有时我需要一系列的步骤来展开.

我会喜欢这个主题的建议和意见.我已经浏览了互联网,但我没有找到足够的例子来帮助我继续这个项目.

提前致谢!

丹尼尔

Roh*_*est 18

这是一个简单的示例,演示了使用MVP设计模式的被动视图的概念.因为我们使用被动视图,所以视图不了解演示者.演示者将简单地订阅视图发布的事件并相应地采取行动.

首先,我们需要为我们的观点定义合同.这通常是使用接口实现的,本质上,我们希望与我们的视图具有非常松散的耦合.我们希望能够切换到不同的视图或事件创建模拟视图以进行单元测试.

这是一份合同,描述了一个用于显示客户信息的简单视图

public interface ICustomerManagementView
{
    void InitializeCustomers(ICustomer[] customers);
    void DisplayCustomer(ICustomer customer);
    event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
Run Code Online (Sandbox Code Playgroud)

它公开了一个方法InitializeCustomers,它将用于使用我们模型中的对象初始化视图.

我们还有一个事件SelectedCustomerChanged,我们的演示者将使用该事件来接收视图中已发生操作的通知.

一旦我们签订了合同,我们就可以开始在演示者中处理这些交互.

public class CustomerManagementPresenter
{
    private ICustomer _selectedCustomer;
    private readonly ICustomerManagementView _managementView;
    private readonly ICustomerRepository _customerRepository;

    public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
    {
        _managementView = managementView;
        _managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;

        _customerRepository = customerRepository;

        _managementView.InitializeCustomers(_customerRepository.FetchCustomers());
    }

    private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
    {
        // Perform some logic here to update the view
        if(_selectedCustomer != args.Value)
        {
            _selectedCustomer = args.Value;
            _managementView.DisplayCustomer(_selectedCustomer);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在演示者中,我们可以使用另一种称为依赖注入的设计模式来访问我们的视图以及我们可能需要的任何模型类.在此示例中,我有一个CustomerRepository,负责获取客户详细信息.

在构造函数中,我们有两行重要的代码,首先我们在视图中订阅了SelectedCustomerChanged事件,我们可以在这里执行相关的操作.其次,我们使用来自存储库的数据调用InitilaizeCustomers.

此时我们还没有为视图定义具体的实现,我们需要做的就是创建一个实现ICustomerManagementView的对象.例如,在Windows窗体应用程序中,我们可以执行以下操作

public partial class CustomerManagementView : Form, ICustomerManagementView
{
    public CustomerManagementView()
    {
        this.InitializeComponents();
    }

    public void InitializeCustomers(ICustomer[] customers)
    {
        // Populate the tree view with customer details
    }

    public void DisplayCustomer(ICustomer customer)
    {
        // Display the customer...
    }

    // Event handler that responds to node selection
    private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
    {
        var customer = e.Node.Tag as ICustomer;
        if(customer != null)
        {
            this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
        }
    }

    // Protected method so that we can raise our event
    protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
    {
        var eventHandler = this.SelectedCustomerChanged;
        if(eventHandler != null)
        {
            eventHandler.Invoke(this, args);
        }
    }

    // Our view will raise an event each time the selected customer changes
    public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
Run Code Online (Sandbox Code Playgroud)

如果我们想测试我们的表示逻辑,我们可以模拟我们的视图并执行一些断言.

编辑:包含自定义事件args

public class EventArgs<T> : EventArgs
{
    private readonly T _value;

    public EventArgs(T value)
    {
        _value = value;
    }

    public T Value
    {
        get { return _value; }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 再次感谢您的帮助.如果客户内部的财产发生变更,例如他/她的地址,那么我们会通过火灾来通知变更.然后,CustomerRepository应该获取该事件,然后它应该通知View需要进行更改.它是否正确?这意味着Customer事件与CustomerRepository事件之间的比率为1-1-1,以查看接收该事件.因此,对于每个更新的属性,有3个事件可以从Customer到View.有没有办法减少这个数字? (2认同)