有時候一個視圖(View)希望得到另外一個視圖顯示的內容,或者選擇的內容。在Eclipse中,比較標準的做法是通過ISelectionProvider和ISelectionListener來完成的。不過因為視圖往往是獨立的,他們之間并不太方便進行直接的事件監聽,而且往往一個視圖需要對諸多試圖進行選擇事件的監聽,因此在這種情況下對每一個視圖的事件進行注冊,比較繁瑣,有時候也不可能(比如在需要被監聽的試圖尚未激活的情況下)。
比較典型的例子就是Eclipse本身所提供的PropertySheet和Outline這兩個視圖,他們都是對其它試圖或者Editor中的選擇進行監聽,并更具選擇的內容作相應的處理,顯示其Outline或者屬性頁。
Eclipse為了解決這個問題,提供了所謂的Site,以及ISelectionService機制,來處理試圖之間的簡單的交互。簡單的說,ViewSite提供了一個交互的中心點,其它View向ViewSite提供選擇事件,或者向其注冊監聽器,而事件的觸發與轉發則由ViewSite()來完成。
這應該也是一個設計模式,不過我還沒想到比較接近的設計模式的名字。如果勉強要使用一個的話,我認為“Mediator”(調停者模式)可能比較適合(歡迎發表見解)。
調停者模式Blabla...
為了在這個機制中扮演角色,視圖通常需要實現兩類接口,或者Adapter。首先是作為被監聽方的視圖,需要實現ISelectionProvider接口。ISelectionProvider是Jface中引入的接口。
public interface ISelectionProvider {
public void addSelectionChangedListener(ISelectionChangedListener
listener);
public ISelection getSelection();
public void removeSelectionChangedListener(
ISelectionChangedListener
listener);
public void setSelection(ISelection selection);
}
方法都比較簡單,不做一一闡述。具體實現時,可能需要進行所謂的hookControl,也就是將View中具體控件的事件,關聯到這個View所提供的ISelectionProvider上,簡單的一個例子,如果View中控件是一個TableViewer的話,那么可以做如下的操作:
protected void hookControl(Control
control) {
tableViewer.addSelectionChangedListener(new
ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent
event) {
ISelection selection2 =
event.getSelection();
setSelection(selection2);
}
});
}
然后再setSelection()中對事件進行擴散(propagate)
public void setSelection(ISelection
selection) {
this.selection = selection;
SelectionChangedEvent event2 = new
SelectionChangedEvent(
OntIndividualEditor.this,
selection);
for (Iterator i =
selectionChangeListeners.iterator(); i.hasNext();) {
ISelectionChangedListener
object = (ISelectionChangedListener) i
.next();
object.selectionChanged(event2);
}
}
一個ISelectionProvider如果希望被別的View進行監聽的話,則應該向其Site()進行注冊:
this.getSite().setSelectionProvider(this);
如果事件比較簡單,比如上面的例子,只是對TableViewer的選擇進行監聽,因為TableViewer本身就是一個ISelectionProvider,因此可以直接這樣做:
this.getSite().setSelectionProvider(tableViewer);
這樣View本身就不必實現ISelectionProvider接口了,但是實現的效果同上面的方式實現的是一樣的。
作為事件監聽的另一端,則更為簡單一些。只需要實現ISelectionListener接口,并注冊在Site中:
site.getPage().addSelectionListener(this);
然后實現public
void selectionChanged(IWorkbenchPart part, ISelection selection) {}方法即可。這樣,當SelectionProvider中的選擇發生改變時,這個視圖中的selectionChanged()方法就會被調用。
注意SelectionProvider和SelectionListener并不直接對應。這個地方有一點容易混淆,就是Eclipse實際上提供有兩套與Selection相關的事件與接口:
ISelectionChangedListener <-->
ISelectionProvider
這個是JFace中的選擇監聽機制,對試圖或者控件而言,它提供對原始的選擇事件的通知與響應。ISelectionProvider需要注冊在Site上,以供ISelectionSerivce使用,將選擇事件擴散到其他的ISelectionListener中。
ISelectionListener <-->
ISelectionService
這個是在Site中使用的,ISelectionService不需要自己實現,已經實現好了,ISelectionListener則需要注冊到ISelectionService上,以對其它SelectionProvider的事件響應進行監聽。
在Eclipse本身的實現中,PropertySheet和Outline都使用了這種機制。不過需要注意的是,缺省的PropertyShee需要接受一個IStructuredSelection,而不僅僅是一個ISelection。因此,如果ISeletionProvider需要提供一個能夠讓PropertySheet進行顯示的對象的話,除了除了要實現ISelection接口外,還需要對其進行封裝成一個IStructuredSelection。這個比較簡單,直接調用StructuredSelection構造函數即可。