如下图,一个可多选的List组件,初始表有3个值,希望实现与一个java.util.List对象(保存选中的值)的双向数据绑定。当List组件中选中的内容变化时,java.util.List对象的内容也同步变化。
我们知道,org.eclipse.jface.databinding.swt.WidgetProperties
工厂类的items()方法中为CCombo、Combo、List提供了获取表中所有条目(item)的observable对象,但是这个observable对象关注的是表中所有条目而不是选中的条目。所以不能满足需求。
参见 org.eclipse.jface.internal.databinding.swt.ListItemsProperty的源码
public class ListItemsProperty extends ControlStringListProperty {
@Override
protected void doUpdateStringList(final Control control, ListDiff diff) {
diff.accept(new ListDiffVisitor() {
List list = (List) control;
@Override
public void handleAdd(int index, Object element) {
list.add((String) element, index);
}
@Override
public void handleRemove(int index, Object element) {
list.remove(index);
}
@Override
public void handleReplace(int index, Object oldElement,
Object newElement) {
list.setItem(index, (String) newElement);
}
});
}
@Override
public String[] doGetStringList(Control control) {
// getter方法调用List.getItems返回的是表中所有内容。
return ((List) control).getItems();
}
@Override
public String toString() {
return "List.items[] " ; //$NON-NLS-1$
}
}
所以如果想实现上面的需求,就得不能用WidgetProperties工厂类提供的observable对象,要动手自己实现Observable类了,好在有了ListItemsProperty 方法的代码,参照它就可以根据自己的需求写一个新的类了。
ListSelectedItemsProperty1.java
package net.gdface.ui;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.core.databinding.property.INativePropertyListener;
import org.eclipse.core.databinding.property.ISimplePropertyListener;
import org.eclipse.jface.internal.databinding.swt.ListItemsProperty;
import org.eclipse.jface.internal.databinding.swt.WidgetListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.List;
/** * List组件中选中项目的Observable对象实现
* 对List组件表中内容的增加,删除操作不会改变表中内容,只会改变对应的selected状态 * @author guyadong * */
public class ListSelectedItemsProperty1 extends ListItemsProperty {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void doUpdateStringList(final Control control, ListDiff diff) {
diff.accept(new ListDiffVisitor() {
List list = (List) control;
@Override
public void handleAdd(int index, Object element) {
// element对应的item置为selected
list.select(find((String) element));
}
@Override
public void handleRemove(int index, Object element) {
// element对应的item置为unselected
list.deselect(find((String) element));
}
@Override
public void handleReplace(int index, Object oldElement,
Object newElement) {
}
/** * 在List中查找指定字符串的索引,找不到则返回-1 * @param element * @return */
private int find(String element){
int count = list.getItemCount ();
for(int i=0;iif(list.getItem(i).equals(element))return i;
}
return -1;
}
});
}
@Override
public String[] doGetStringList(Control control) {
// 返回所有选中的item
return ((List) control).getSelection();
}
// 重写adaptListener方法,返回NativePropertyListener对象,
// 否则外部不能检测到List组件的状态变化
@SuppressWarnings({ "rawtypes" })
@Override
public INativePropertyListener adaptListener(ISimplePropertyListener listener) {
return new WidgetListener(this, listener, new int[]{SWT.Selection}, null);
}
}
TestList.java
package testwb;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.sideeffect.ISideEffect;
import org.eclipse.jface.databinding.swt.DisplayRealm;
import org.eclipse.jface.databinding.swt.ISWTObservableList;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import net.gdface.ui.ListSelectedItemsProperty1;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.SWT;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class TestList extends Dialog {
private DataBindingContext m_bindingContext;
// 数据对象
private WritableList observableList=new WritableList();
private List list;
private Label lblSelected;
/** * Create the dialog. * @param parentShell */
public TestList(Shell parentShell) {
super(parentShell);
}
/** * Create contents of the dialog. * @param parent */
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
container.setLayout(null);
// 初始化数据对象
observableList.add("apple");
list = new List(container, SWT.BORDER | SWT.MULTI);
// 初始化List组件中的内容
list.add("apple");
list.add("orange");
list.add("banana");
list.setBounds(10, 31, 231, 68);
lblSelected = new Label(container, SWT.BORDER);
lblSelected.setBounds(10, 153, 424, 32);
Label label = new Label(container, SWT.NONE);
label.setBounds(10, 130, 61, 17);
label.setText("已选择");
Button btnAdd = new Button(container, SWT.NONE);
btnAdd.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if(!observableList.contains("banana"))observableList.add("banana");
}
});
btnAdd.setBounds(286, 30, 80, 27);
btnAdd.setText("增加");
Button btnClear = new Button(container, SWT.NONE);
btnClear.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
observableList.clear();
}
});
btnClear.setBounds(286, 105, 80, 27);
btnClear.setText("清除");
Button btnDelete = new Button(container, SWT.NONE);
btnDelete.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
observableList.remove("apple");
}
});
btnDelete.setBounds(286, 63, 80, 27);
btnDelete.setText("删除");
return container;
}
/** * Create contents of the button bar. * @param parent */
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
m_bindingContext = initDataBindings();
}
/** * Return the initial size of the dialog. */
@Override
protected Point getInitialSize() {
return new Point(450, 300);
}
public static void main(String[] args) {
Display display = Display.getDefault();
Realm.runWithDefault(DisplayRealm.getRealm(display), new Runnable() {
public void run() {
try {
TestList testList = new TestList(null);
testList.open();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
protected DataBindingContext initDataBindings() {
DataBindingContext bindingContext = new DataBindingContext();
// 调用ListSelectedItemsProperty1为List组件创建observable对象
ISWTObservableList observeSelectedListObserveWidget =new ListSelectedItemsProperty1().observe(list);
// 将List组件与数据对象observableList绑定在一起
bindingContext.bindList(observeSelectedListObserveWidget, observableList, null, null);
// 为查看数据对象observableList的内容,将之与Label组件进行单向绑定,
// observableList的内容变化能及时显示在Label中
ISideEffect.create(
observableList::size, (s)->{
lblSelected.setText(String.join(",", observableList));
});
return bindingContext;
}
}
ListSelectedItemsProperty1.java的父类org.eclipse.jface.internal.databinding.swt.ListItemsProperty
在jface.internal包下,也就是非公开的包,所以这个类的接口稳定性并没有保证,版本升级的时候有可能会被改变或删除。
ListSelectedItemsProperty1中用到的WidgetListener类也是同样的问题。
如果要解决这个问题,应该把该类及其父类的代码复制出来重写才是最保险的。
如下为以WidgetListProperty为父类重写的ListSelectedItemsProperty2,不使用jface.internal包下的类
ListSelectedItemsProperty2.java
package net.gdface.ui;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.core.databinding.property.INativePropertyListener;
import org.eclipse.core.databinding.property.ISimplePropertyListener;
import org.eclipse.core.databinding.property.NativePropertyListener;
import org.eclipse.jface.databinding.swt.WidgetListProperty;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
public class ListSelectedItemsProperty2 extends WidgetListProperty {
@Override
public Object getElementType() {
return String.class;
}
@SuppressWarnings("rawtypes")
@Override
protected void doSetList(Object source, List list, ListDiff diff) {
doUpdateList(source, diff);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void doUpdateList(Object source, ListDiff diff) {
diff.accept(new ListDiffVisitor() {
org.eclipse.swt.widgets.List list = (org.eclipse.swt.widgets.List) source;
@Override
public void handleAdd(int index, Object element) {
list.select(find((String) element));
}
@Override
public void handleRemove(int index, Object element) {
list.deselect(find((String) element));
}
@Override
public void handleReplace(int index, Object oldElement,
Object newElement) {
}
/** * 在List中查找指定字符串的索引,找不到则返回-1 * @param element * @return */
private int find(String element){
int count = list.getItemCount ();
for(int i=0;iif(list.getItem(i).equals(element))return i;
}
return -1;
}
});
}
@SuppressWarnings("rawtypes")
@Override
protected List doGetList(Object source) {
return Arrays.asList(((org.eclipse.swt.widgets.List) source).getSelection());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public INativePropertyListener adaptListener(ISimplePropertyListener listener) {
// 继承NativePropertyListener实现INativePropertyListener 接口
return new NativePropertyListener(this,listener){
private final SelectionListener selectionListener=new SelectionListener(){
@Override
public void widgetSelected(SelectionEvent e) {
fireChange(e.getSource(), null);
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
fireChange(e.getSource(), null);
}};
@Override
protected void doAddTo(Object source) {
((org.eclipse.swt.widgets.List)source).addSelectionListener(selectionListener);
}
@Override
protected void doRemoveFrom(Object source) {
((org.eclipse.swt.widgets.List)source).removeSelectionListener(selectionListener);
}
};
}
}