在定制EclipseSWT組件中實現MVC
Eclipse SWT(標準部件工具包)提供了豐富的 API 集來實現定制部件(widget)。在這篇文章中,作者簡要介紹了 MVC(模型-視圖-控制器)架構,以結構化查看器的形式解釋了 MVC 的當前實現,并介紹了一種使用定制 SWT 部件的實現。
什么是 MVC?
MVC 架構(或設計模式)是圖形用戶界面(GUI)的設計樣式,由三部分構成:模型、視圖和控制器。MVC 把表示層從數據解耦出來,也把表示從數據的操作解耦出來。
實現 MVC 架構與其他類型的應用程序有所不同。主要的區別來自如何放置和實現業務邏輯或查看呈現邏輯。與典型的 Web 應用程序不同,在這類程序中,程序員必須設計和實現所有 MVC 組件,而 Eclipse 提供的 API 可以替您做大部分控制或呈現工作。所以,不能嚴格地把 Eclipse 的 MVC 實現與 Web 或其他應用程序類型的 MVC 進行比較。
Eclipse JFace
Eclipse JFace 用內容提供者和標簽提供者實現 MVC 架構。JFace API 包裝了標準(并非不重要的)部件,例如表和樹,并實現了結構化內容提供者和標簽提供者。可以根據部件類型實現不同的內容提供者。面向列表的查看器會實現結構化查看器,而內容則以結構化(列表的)方式映射到部件條目上。
基類叫做 Viewer,它是結構化查看器的一個擴展。查看器充當部件容器。內容提供者以結構化的方式得到數據;類似地,標簽提供者獲得對應的標簽。JFace 查看器實現檢索該數據,設置對應的關聯,并用數據集更新用戶界面(UI)組件。它還執行選擇、過濾和排序。
如何實現 JFace
Eclipse View 和 Viewer 負責執行大部分 JFace 控制功能。Viewer 或者說 MVC 的視圖部分,也充當部件容器;這是表示組件。
Eclipse View 實例化 Viewer、內容提供者和標簽提供者,并充當模型,容納值對象,并在 Viewer 中把它們設置為 inputElement。
要創建 View,請用 createPartControl() 方法實例化 Viewer。清單 1 實例化一個默認的樹查看器;您也可以定制樹,并用樹對象作為參數,用構造函數實例化樹查看器。
清單 1. ExampleView 的 CreatePartControl 方法
在另一個獨立類中實現 ContentProvider,它是一個對象,用適合查看器類型的接口向視圖提供數據。例如,您可以實現 IStructuredContentProvider 或 ITreeContentProvider 查看器。
請在 ContentProvider 代碼中實現以下一個方法,把 ContentProvider 與 Viewer 相關聯:
注意:JFace 框架將調用這些方法。
清單 2. 創建定制的 ContentProvider
MVC 架構通常包含多個視圖和一個數據源。目前在 Eclipse 平臺上,只能把一個視圖與一個模型相關聯。但是,也可以創建多個視圖,用適配器視圖訪問同一數據。只要把 inputChanged() 方法包含在 ContentProvider 類中即可。只要 Viewer 有新的輸入集,就會使用 inputChanged() 方法通知 ContentProvider。inputChanged() 方法接受 Viewer 作為輸入參數,所以多個視圖可以使用一個 ContentProvider。
清單 3. 將 inputChanged 方法用于不同的查看器
與 Eclipse SWT 結合使用 MVC
在多數常見 GUI 應用程序中,創建布局來顯示請求的數據,或完成表單(例如用戶界面)來添加或修改數據。圖 1 的示例應用程序演示了如何在定制表單中,用只讀和可編寫模式顯示來自 XML 存儲的數據。它還解釋了每個組件相對于 MVC 架構的角色。
圖 1. 示例應用程序

圖 2 顯示了應用程序的類圖,有助于更好地理解整體架構。
圖 2. 示例應用程序的類圖

創建控件
ExampleView 充當整個應用程序的容器。它將在 createPartControl 方法中初始化應用程序。
清單 4. CreatePartControl 方法初始化布局
創建表單和布局
基本布局類定義了不同的表單應用程序使用的全局方法和聲明。有些充當回調機制的容器事件,也注冊到了這里。
清單 5. 布局的 CreateControl 方法
創建字段(視圖)
Field 是一個抽象類,它定義了包含各種用戶界面控件的方法,還有全局地識別這些控件的相關 ID。每個用戶界面控件都是 Field 的子類,并向內容提供者提供了讀寫能力。清單 6 用工廠模式,在 Layout 類中創建了 Field。
清單 6. 用 Field 類創建文本對象
簡化內容提供者(模型)
ExampleViewContentProvider 充當模型偵聽器,后者擴展自 IStructuredContentProvider。它是 Eclipse API 的簡單實現,提供了用于檢索數據的回調。每個請求數據的條目都基于視圖創建時在布局中為條目定義的惟一 ID。
方法調用會返回與每個定義的全局 ID 關聯的數據。在 清單 7 所示的內容提供者中,可以使用適配器從 XML 文件或數據庫檢索數據。
清單 7. 在定制的 ContentProvider 中實現方法
創建了控件并初始化布局之后,表單會用控件 ID 要求內容提供者用數據填充表單控件。
清單 8. 初始化布局并填充控件的表單
結束語
Web 應用程序是 MVC 架構樣式的早期實現者。但是,隨著像 Eclipse 這樣的簡單而強大的開發平臺的到來,程序員可以輕易地用更短的時間和最小的復雜程度,開發出更豐富的用戶界面。
什么是 MVC?
MVC 架構(或設計模式)是圖形用戶界面(GUI)的設計樣式,由三部分構成:模型、視圖和控制器。MVC 把表示層從數據解耦出來,也把表示從數據的操作解耦出來。
實現 MVC 架構與其他類型的應用程序有所不同。主要的區別來自如何放置和實現業務邏輯或查看呈現邏輯。與典型的 Web 應用程序不同,在這類程序中,程序員必須設計和實現所有 MVC 組件,而 Eclipse 提供的 API 可以替您做大部分控制或呈現工作。所以,不能嚴格地把 Eclipse 的 MVC 實現與 Web 或其他應用程序類型的 MVC 進行比較。
Eclipse JFace
Eclipse JFace 用內容提供者和標簽提供者實現 MVC 架構。JFace API 包裝了標準(并非不重要的)部件,例如表和樹,并實現了結構化內容提供者和標簽提供者。可以根據部件類型實現不同的內容提供者。面向列表的查看器會實現結構化查看器,而內容則以結構化(列表的)方式映射到部件條目上。
基類叫做 Viewer,它是結構化查看器的一個擴展。查看器充當部件容器。內容提供者以結構化的方式得到數據;類似地,標簽提供者獲得對應的標簽。JFace 查看器實現檢索該數據,設置對應的關聯,并用數據集更新用戶界面(UI)組件。它還執行選擇、過濾和排序。
如何實現 JFace
Eclipse View 和 Viewer 負責執行大部分 JFace 控制功能。Viewer 或者說 MVC 的視圖部分,也充當部件容器;這是表示組件。
Eclipse View 實例化 Viewer、內容提供者和標簽提供者,并充當模型,容納值對象,并在 Viewer 中把它們設置為 inputElement。
要創建 View,請用 createPartControl() 方法實例化 Viewer。清單 1 實例化一個默認的樹查看器;您也可以定制樹,并用樹對象作為參數,用構造函數實例化樹查看器。
清單 1. ExampleView 的 CreatePartControl 方法
public class ExampleView extends ViewPart { ... public void createPartControl(Composite parent) { // define a grid layout GridLayout layout = new GridLayout(); layout.numColumns = 1; layout.marginHeight = 0; layout.marginWidth = 0; l ayout.horizontalSpacing = 0; layout.verticalSpacing = 1; parent.setLayout(layout); // create widgets createActionBar(parent); createTree(parent); // add context menu and listeners viewer.addDoubleClickListener(this); viewer.addSelectionChangedListener(openAction); // register viewer so actions respond to selection getSite().setSelectionProvider(viewer); hookContextMenu(); } private void createTree(Composite parent) { viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); viewer.setContentProvider(new ExampleViewContentProvider()); viewer.setLabelProvider (new ExampleViewLabelProvider()); viewer.setSorter(new ViewerSorter()); viewer.setInput(ModelManager.getExampleModel()); viewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); } ... } |
在另一個獨立類中實現 ContentProvider,它是一個對象,用適合查看器類型的接口向視圖提供數據。例如,您可以實現 IStructuredContentProvider 或 ITreeContentProvider 查看器。
請在 ContentProvider 代碼中實現以下一個方法,把 ContentProvider 與 Viewer 相關聯:
- getElements(Object parent)
- getChildren(Object element)
注意:JFace 框架將調用這些方法。
清單 2. 創建定制的 ContentProvider
public class ExampleViewContentprovide implements ITreeContentProvide { |
MVC 架構通常包含多個視圖和一個數據源。目前在 Eclipse 平臺上,只能把一個視圖與一個模型相關聯。但是,也可以創建多個視圖,用適配器視圖訪問同一數據。只要把 inputChanged() 方法包含在 ContentProvider 類中即可。只要 Viewer 有新的輸入集,就會使用 inputChanged() 方法通知 ContentProvider。inputChanged() 方法接受 Viewer 作為輸入參數,所以多個視圖可以使用一個 ContentProvider。
清單 3. 將 inputChanged 方法用于不同的查看器
/** * Register content provider with model. */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput != null) { this.viewer = viewer; this.model = (ExampleDelegate)newInput; this.model.addModelListener(this); } } |
與 Eclipse SWT 結合使用 MVC
在多數常見 GUI 應用程序中,創建布局來顯示請求的數據,或完成表單(例如用戶界面)來添加或修改數據。圖 1 的示例應用程序演示了如何在定制表單中,用只讀和可編寫模式顯示來自 XML 存儲的數據。它還解釋了每個組件相對于 MVC 架構的角色。
圖 1. 示例應用程序

圖 2 顯示了應用程序的類圖,有助于更好地理解整體架構。
圖 2. 示例應用程序的類圖

創建控件
ExampleView 充當整個應用程序的容器。它將在 createPartControl 方法中初始化應用程序。
清單 4. CreatePartControl 方法初始化布局
public void createPartControl(Composite parent) { ExampleEditLayout _layout = new ExampleEditLayout(parent,SWT.NONE,FieldMode.Read,new ExampleViewContentProvider()); } |
創建表單和布局
基本布局類定義了不同的表單應用程序使用的全局方法和聲明。有些充當回調機制的容器事件,也注冊到了這里。
清單 5. 布局的 CreateControl 方法
public void createControls(int style) { GridData gridData; Text textFld, subjectFld; Control toLabel, ccLabel, bccLabel; Control fromDateTime; Control control; Button durationText; Button submit; GridLayout layout = new GridLayout(2, false); layout.marginWidth = 0; layout.marginHeight = 4; setLayout(layout); //Label gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 10; LabelFactory.create(this, Messages.getString("ExampleEditLayout.Title"), gridData); //$NON-NLS-1$ gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 40; LabelFactory.create(this, "", gridData); //Text gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 10; control = LabelFactory.create(this, Messages.getString("ExampleEditLayout.Email"), gridData); //$NON-NLS-1$ gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 10; control = TextFactory.create(this, SWT.BORDER | SWT.V_SCROLL | SWT.WRAP, gridData, FieldMode.Edit); //$NON-NLS-1$ addField(new TextField(control, ExampleViewContentProvider.FIRST_INDEX)); //Combo gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 10; LabelFactory.create(this, Messages.getString("ExampleEditLayout.Group"), gridData); //$NON-NLS-1$ gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_CENTER); gridData.horizontalIndent = 40; control = ComboFactory.create(this, FieldMode.Edit, false, gridData); //$NON-NLS-1$ addField(new ComboField(control, ExampleViewContentProvider.SECOND_INDEX)); ...} |
創建字段(視圖)
Field 是一個抽象類,它定義了包含各種用戶界面控件的方法,還有全局地識別這些控件的相關 ID。每個用戶界面控件都是 Field 的子類,并向內容提供者提供了讀寫能力。清單 6 用工廠模式,在 Layout 類中創建了 Field。
清單 6. 用 Field 類創建文本對象
public class TextField extends Field { /** * @param control * @param id */ public TextField(Control control, int id) { super(control, id); } /* Based on the ID of the widget, values retrieved from * the content provider are set. */ public void readFromContent(IExampleContentProvider content) { String newText = (String )content.getElement(getId()); if (newText != null) ((Text )_control).setText(newText); } /* Based on the ID of the widget, values retrieved from widget are * sent back to the content provider. */ public void writeToContent(IExampleContentProvider content) { String newText = ((Text )_control).getText(); content.setElement(getId(), newText); } } |
簡化內容提供者(模型)
ExampleViewContentProvider 充當模型偵聽器,后者擴展自 IStructuredContentProvider。它是 Eclipse API 的簡單實現,提供了用于檢索數據的回調。每個請求數據的條目都基于視圖創建時在布局中為條目定義的惟一 ID。
方法調用會返回與每個定義的全局 ID 關聯的數據。在 清單 7 所示的內容提供者中,可以使用適配器從 XML 文件或數據庫檢索數據。
清單 7. 在定制的 ContentProvider 中實現方法
public Object getElement(int iIndex) { switch (iIndex) { case FIRST_INDEX: return "developer@ibm.com"; case SECOND_INDEX : return new Integer(1); case FOURTH_INDEX : return new Boolean(true); case THIRD_INDEX: return new Boolean(false); case FIFTH_INDEX: return new Boolean(false); } return null; } |
創建了控件并初始化布局之后,表單會用控件 ID 要求內容提供者用數據填充表單控件。
清單 8. 初始化布局并填充控件的表單
public Form (Composite parent, int style, FieldMode mode, ExampleViewContentProvider content) { super(parent, style); _content = content; _style = style; setMode(mode); init(style); } private void init(int style) { createControls(style); controlsCreated(); } protected void controlsCreated() { readFromContent(); } |
結束語
Web 應用程序是 MVC 架構樣式的早期實現者。但是,隨著像 Eclipse 這樣的簡單而強大的開發平臺的到來,程序員可以輕易地用更短的時間和最小的復雜程度,開發出更豐富的用戶界面。