C#WinForms Model-View-Presenter(被动视图) [英] C# WinForms Model-View-Presenter (Passive View)

查看:117
本文介绍了C#WinForms Model-View-Presenter(被动视图)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在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被动视图设计模式



我试图使它使程序独立于视图工作。我没有找到任何一个例子,其中一个更复杂的互动视图与被动视图模式一起使用。



问题:



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



2)当谈到将事件从视图传递给演示者/控制器(无论什么术语)希望使用WRT被动视图设计模式),我应该怎么做?有时候我有简单的属性需要更新,有时我需要一系列的步骤展开。



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



提前感谢!



Daniel

解决方案

这是一个简单的例子,演示了使用MVP设计模式的被动视图的概念。因为我们使用被动的观点,所以这个观点并不了解主持人。主持人将简单地订阅该视图发布的事件,并据此采取相应行动。



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



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

  public interface ICustomerManagementView 
{
void InitializeCustomers(ICustomer [] customers))
void DisplayCustomer(客户客户);
事件EventHandler< EventArgs< ICustomer>> SelectedCustomerChanged;
}

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



我们还有一个事件 SelectedCustomerChanged ,将由我们的主持人接收通知在视图中发生了一个动作。



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

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

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

_customerRepository = customerRepository;

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

private void SelectedCustomerChanged(object sender,EventArgs< ICustomer> args)
{
//执行一些逻辑来更新视图
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)
{
//填充具有客户详细信息的树视图
}

public void DisplayCustomer(客户客户)
{
//显示客户...
}

//响应节点选择的事件处理程序
private void CustomerTreeViewAfterSelect(object sender,TreeViewEventArgs e)
{
var customer = e.Node.Tag as ICustomer;
if(customer!= null)
{
this.OnSelectedCustomerChanged(new EventArgs< ICustomer>(customer));
}
}

//保护方法,以便我们可以提高我们的事件
protected virtual void OnSelectedCustomerChanged(EventArgs< ICustomer> args)
{
var eventHandler = this.SelectedCustomerChanged;
if(eventHandler!= null)
{
eventHandler.Invoke(this,args);
}
}

//我们的视图将在每次所选客户更改时引发事件
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; }
}
}


I'm developing a WinForms application in C#. I have limited experience in GUI programming, and I am having to learn a great deal on the fly. That being said, here's what I am building.

See the general GUI look at the following link:

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

Now, I have done a lot of the work already, but in the very bad Autonomous design pattern. I did not know the project would ever reach a certain size, and, as such, it is time to do some major refactoring.

I have been studying a great deal about GUI design patterns, and the pattern I am wishing to implement is the Passive View (see http://martinfowler.com/eaaDev/PassiveScreen.html). I am looking for some help on how to bring this all together.

Background:

1) Depending on what the user clicks in the "TreeView", the "List" in the bottom left-hand corner will display a list of objects that can populate the "Editor" area. These objects might be a TextBox or a DataGridView. The user toggles the List to choose what he/she wants to see in the "Editor"

2) The model is essentially a folder with data and configuration files. There is an external program that runs on a given directory, creates output files/folders, etc. This program I am developing is designed to effectively manage/configure these objects in a user-friendly way

3) The problem with the way I have been doing things is that it is next to impossible to test, and hence the move to the MVP-esque Passive View design pattern

I am trying to make it so that the program works independently of the View. I have not been able to find any examples where a more complex, interactive view is used with the Passive View pattern.

Questions:

1) Do I need to implement one large interface/view for the entire "look" of the program, then implement sub-interfaces/sub-views for each of the TreeView, Editor, Logger, etc.? Or is there a better "structure" to doing this?

2) When it comes to "handing off" events from the View to the Presenter/Controller (whatever terminology you wish to use W.R.T. the Passive View design pattern), what is the way I should be doing this? Sometimes I have simple properties that need to be updated, and sometimes I need a whole series of steps to unfold.

I would love suggestions and advice on this topic. I have scoured the Internet, and I haven't found adequate examples to help me continue with this project.

Thanks in advance!

Daniel

解决方案

Here is a simple example that demonstrates the concept of passive views using the MVP design pattern. Because we are using passive views the view has no knowledge of the presenter. The presenter will simply subscribe to events published by the view and act accordingly.

To start out we need to define a contract for our view. This is typically achieved using an interface, essentially, we want to have a very loose coupling with our view. We want the ability to switch to different views or event create mock views for unit testing.

Here is a contract that describes a simple view that will be used to display customer information

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

It exposes a single method InitializeCustomers that will be used to initialize our view with objects from our model.

We also have an event SelectedCustomerChanged that will be used by our presenter to receive notification that an action has occurred in the view.

Once we have our contract we can start to handle these interactions in our 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);
        }
    }
}

In the presenter we can use another design pattern called dependency injection to provide access to our view and any model classes that we may need. In this example I have a CustomerRepository that is responsible for fetching customer details.

In the constructor we have two important lines of code, firstly we have subscribed to the SelectedCustomerChanged event in our view, it is here that we can perform associated actions. Secondly we have called InitilaizeCustomers with data from the repository.

At this point we haven't actually defined a concrete implementation for our view, all we need to do is create an object that implements ICustomerManagementView. For example in a Windows Forms application we can do the following

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

If we wanted to test our presentation logic we could mock our view and perform some assertions.

EDIT : Included custom event args

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

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

    public T Value
    {
        get { return _value; }
    }
}

这篇关于C#WinForms Model-View-Presenter(被动视图)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆