2010-11-30 65 views
20

我正在C#中開發一個WinForms應用程序。我在GUI編程方面的經驗有限,而且我不得不在飛行中學習很多東西。話雖如此,這就是我正在建設的東西。C#WinForms Model-View-Presenter(被動視圖)

看到一般GUI看看下面的鏈接:

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

現在,我已經做了很多的工作了,但在非常糟糕的自主設計模式。我不知道該項目會達到一定的規模,因此,現在是時候進行一些重大的重構。

我一直在研究GUI設計模式,我希望實現的模式是被動視圖(見http://martinfowler.com/eaaDev/PassiveScreen.html)。我正在尋找一些關於如何將這一切結合在一起的幫助。

背景:

1)根據什麼在「樹視圖」的用戶點擊,「列表」在底部左下角會顯示可填充「編輯器」區域對象的列表上。這些對象可能是一個TextBox或一個DataGridView。用戶切換列表以選擇他/她想要在「編輯器」中看到的內容

2)該模型本質上是一個包含數據和配置文件的文件夾。有一個外部程序運行在給定的目錄,創建輸出文件/文件夾等。我正在開發的這個程序旨在以一種用戶友好的方式有效地管理/配置這些對象

3)我一直在做的事情是,它是幾乎不可能的測試,因此轉移到MVP類似被動視圖設計模式

我想讓它使程序獨立於視圖工作。我一直無法找到任何一個更復雜的交互式視圖與被動視圖模式一起使用的例子。

問題:

1)我需要實現一個大接口/查看整個「看」的程序,然後實現子接口/子視圖每個樹視圖,編輯器,記錄儀等等?還是有更好的「結構」來做到這一點? 2)當將視圖中的事件「交付」給演示者/控制器(無論您希望使用W.R.T.被動視圖設計模式的術語),我應該如何做到這一點?有時候我需要更新簡單的屬性,有時我需要一系列的步驟來展開。

我很想聽到關於這個話題的建議和建議。我搜索了互聯網,但我還沒有找到足夠的例子來幫助我繼續這個項目。

在此先感謝!

丹尼爾

回答

18

下面是一個簡單的例子,表明了使用MVP設計圖案被動視圖的概念。因爲我們使用被動視圖,所以視圖不知道演示者。主持人只需訂閱該視圖發佈的事件並採取相應行動。

首先,我們需要爲我們的視圖定義合同。這通常是通過一個接口實現的,本質上,我們希望與我們的視圖有一個非常鬆散的耦合。我們希望能夠切換到不同的視圖或事件,爲單元測試創​​建模擬視圖。

這裏是描述將用來顯示客戶信息

public interface ICustomerManagementView 
{ 
    void InitializeCustomers(ICustomer[] customers); 
    void DisplayCustomer(ICustomer customer); 
    event 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 = 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); 
     } 
    } 
} 

在我們可以使用一個叫做dependency injection提供訪問我們的觀點,並且我們可能需要的任何模型類的另一個設計模式的演講。在這個例子中,我有一個負責獲取客戶細節的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; } 
    } 
} 
+0

我很欣賞信息豐富的答覆,但它確實解決了我的問題。我是否應該使用一個View(用戶可以與這些4-5個子組件交互),還是應該在Views中有Views(因此接口內的接口)?此外,我仍然不確定將視圖中的事件「交付給」演示者,我仍然需要澄清View如何觀察模型。我也很關心程序的「結構」。 – 2010-11-30 21:43:26

+1

我更喜歡把它分解成更小的組件,特別是如果你想鼓勵重用。創建具有單一用途的視圖,但可以一起使用以創建複合UI – 2010-11-30 21:51:18

0

我會打破他們與自己的禮物獨立意見,並使用「控制」主持人/視圖來管理他們之間的一切消息代表團。這不僅可以幫助可測試性,而且還可以讓您的控件實現SRP。

所以你的情況,你可能有一個IFormManager其主窗口將實施,然後一個IFileManager,ILoggerWindow等等,等等

雖然它可能是一個有點矯枉過正使用,我建議你有看看智能客戶端軟件工廠(來自微軟模式和實踐團隊) - 它不再被積極開發,但它具有很好的MVP實現,並且能夠很好地完成這種視圖組合的工作,所以可能會給你一些好的想法。