20

我正在用 C# 开发一个 WinForms 应用程序。我在 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 Passive View 设计模式

我正在努力使程序独立于视图工作。我找不到任何将更复杂的交互式视图与被动视图模式一起使用的示例。

问题:

1)我是否需要为程序的整个“外观”实现一个大界面/视图,然后为每个 TreeView、Editor、Logger 等实现子接口/子视图?还是有更好的“结构”来做到这一点?

2)当涉及从视图“移交”事件到演示者/控制器(无论您希望使用 WRT 被动视图设计模式的任何术语)时,我应该以什么方式执行此操作?有时我有一些简单的属性需要更新,有时我需要一系列的步骤来展开。

我很想就这个话题提出建议和建议。我搜索了互联网,但没有找到足够的例子来帮助我继续这个项目。

提前致谢!

丹尼尔

4

2 回答 2

18

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

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

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

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

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

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

一旦我们有了合约,我们就可以开始在 Presenter 中处理这些交互。

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

在演示器中,我们可以使用另一种称为依赖注入的设计模式来提供对我们的视图和我们可能需要的任何模型类的访问。在此示例中,我有一个负责获取客户详细信息的 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;
}

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

编辑:包括自定义事件参数

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

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

    public T Value
    {
        get { return _value; }
    }
}
于 2010-11-30T20:11:20.763 回答
0

我会将它们分解为带有自己呈现的单独视图,并使用“控制”演示者/视图来管理它们之间的消息委托。这不仅有助于可测试性,而且还可以使您的控件满足 SRP。

因此,在您的情况下,您可能有一个主窗口将实现的 IFormManager,然后是 IFileManager、ILoggerWindow 等。

虽然使用起来可能有点矫枉过正,但我​​建议您看看 Smart Client Software Factory(来自 Microsoft 模式和实践团队)——它不再被积极开发,但它有一个很好的 MVP 和这种视图组合的东西做得很好,所以可能会给你一些好主意。

于 2010-12-02T02:03:57.480 回答