Web MVC 中程序員需手工構建三層,而 Eclipse RCP 通過 JFace Viewer 框架內建了大量控制/呈現邏輯。開發者核心任務是實現業務模型與視圖組件的綁定適配,而非從零構建 MVC。
| MVC 概念 | JFace 實現 | 職責 |
|---|---|---|
| Model | ContentProvider + 領域對象 | 提供結構化數據、監聽模型變更 |
| View | Viewer (TreeViewer/TableViewer) + SWT Widgets | 渲染 UI、處理選擇/過濾/排序 |
| Controller | ViewPart + LabelProvider + 監聽器 | 協調模型與視圖、處理用戶交互 |
public class ExampleView extends ViewPart {
private TreeViewer viewer;
public void createPartControl(Composite parent) {
// 1. 佈局
GridLayout layout = new GridLayout();
parent.setLayout(layout);
// 2. 實例化 Viewer(視圖容器)
viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
// 3. 設置提供者(模型適配層)
viewer.setContentProvider(new ExampleViewContentProvider());
viewer.setLabelProvider(new ExampleViewLabelProvider());
viewer.setSorter(new ViewerSorter());
// 4. 輸入模型根對象
viewer.setInput(ModelManager.getExampleModel());
// 5. 註冊選擇提供者,讓動作響應選擇
getSite().setSelectionProvider(viewer);
hookContextMenu(); // 上下文菜單
}
}
IStructuredContentProvider:列表/表格型視圖,getElements(input) 返回扁平集合ITreeContentProvider:樹型視圖,需實現:
getElements(Object input):根節點getChildren(Object parent):子節點getParent(Object child):父節點(可選)hasChildren(Object element):是否有子節點
public class ExampleViewContentProvider implements ITreeContentProvider {
private Viewer viewer;
private ExampleDelegate model;
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (newInput != null) {
this.viewer = viewer;
this.model = (ExampleDelegate) newInput;
this.model.addModelListener(this); // 監聽模型變更,刷新視圖
}
}
// ... getElements/getChildren 實現
}
Viewer 每次 setInput() 都會調用 inputChanged(),實現一模型多視圖同步。
// 抽象字段基類
public abstract class Field {
protected Control _control;
protected int _id;
public Field(Control control, int id) { this._control = control; this._id = id; }
// 從模型讀取 → UI
public abstract void readFromContent(IExampleContentProvider content);
// 從 UI 寫回 → 模型
public abstract void writeToContent(IExampleContentProvider content);
}
// 文本字段實現
public class TextField extends Field {
public TextField(Control control, int id) { super(control, id); }
public void readFromContent(IExampleContentProvider content) {
String text = (String) content.getElement(getId());
if (text != null) ((Text)_control).setText(text);
}
public void writeToContent(IExampleContentProvider content) {
content.setElement(getId(), ((Text)_control).getText());
}
}
// Combo/Checkbox 等同理擴展
public class TextFactory {
public static Control create(Composite parent, int style, GridData gd, FieldMode mode) {
Text text = new Text(parent, style);
text.setLayoutData(gd);
return text;
}
}
// Layout 類中調用
Control control = TextFactory.create(this, SWT.BORDER, gridData, FieldMode.Edit);
addField(new TextField(control, ExampleViewContentProvider.FIRST_INDEX));
public class Form extends Composite {
private IExampleContentProvider _content;
public Form(Composite parent, int style, FieldMode mode, IExampleContentProvider content) {
super(parent, style);
_content = content;
setMode(mode);
init(style);
}
private void init(int style) {
createControls(style);
controlsCreated(); // 關鍵鉤子
}
protected void controlsCreated() {
readFromContent(); // 遍歷所有字段調用 readFromContent()
}
private void readFromContent() {
for (Field field : fields) {
field.readFromContent(_content);
}
}
public void save() {
for (Field field : fields) {
field.writeToContent(_content); // 提交時寫回模型
}
}
}
public class ExampleViewContentProvider implements IExampleContentProvider {
public static final int FIRST_INDEX = 0; // Email
public static final int SECOND_INDEX = 1; // Group
// ...
public Object getElement(int index) {
switch (index) {
case FIRST_INDEX: return "developer@ibm.com";
case SECOND_INDEX: return new Integer(1);
case THIRD_INDEX: return Boolean.FALSE;
// ...
}
return null;
}
public void setElement(int index, Object value) {
// 實際項目中持久化到 XML/數據庫
// 並通知監聽器模型已變更
}
}
readFromContent/writeToContent 協議,支援任意控件類型Eclipse RCP 的 MVC 實踐核心在於利用框架內建的 Controller/View 基礎設施,開發者專注於 ContentProvider/LabelProvider/Field 三個適配層的實現。這種「約定優於配置」的設計,配合 SWT/JFace 豐富的 Widget 庫,能以極低複雜度構建企業級富客戶端應用。