Binding类中的updateModelToTarget方法,就是实现从数据对象到目标对象(比如Widget)的更新方法,只要调用这个方法就能强制让数据对象的内容同步到目标对象。如下是抽象方法updateModelToTarget的说明:
/** * Updates the target's state from the model's state at the next reasonable * opportunity. There is no guarantee that the state will have been updated * by the time this call returns. * 在合适的机会将model状态更新到target,因为是异步更新,所以不保证当方法返回时target更新完成。 */
public abstract void updateModelToTarget();
如下代码显示一个简单的对话框,Text文本框中初始是个浮点数0.5,当修改文本框中的内容不符合float数值格式时,左上角会提示出错。点击下面的”恢复初始值”按钮,希望能恢复初始值0.5。
package testwb;
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.Shell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Text;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.swt.widgets.Display;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
import org.eclipse.jface.databinding.swt.DisplayRealm;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class TestPojoBinding5 extends Dialog {
private DataBindingContext m_bindingContext;
// floatValue 初始值0.5f
private final WritableValue floatValue = new WritableValue(Float.valueOf(0.5f), Float.class);
// Text文本框对象
private Text floatValueText;
private Binding bindValue;
/** * Create the dialog. * @param parentShell */
public TestPojoBinding5(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);
Button btnNewButton = new Button(container, SWT.NONE);
// 点击“恢复初始值”按钮时,将floatValue重新设置为初始值
btnNewButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
floatValue.setValue(Float.valueOf(0.5f));
}
});
btnNewButton.setBounds(38, 154, 80, 27);
btnNewButton.setText("恢复初始值");
floatValueText = new Text(container, SWT.BORDER);
floatValueText.setBounds(38, 27, 80, 23);
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(362, 298);
}
public static void main(String[] args) {
Display display = Display.getDefault();
Realm.runWithDefault(DisplayRealm.getRealm(display), new Runnable() {
public void run() {
try {
TestPojoBinding5 setting = new TestPojoBinding5(null);
setting.open();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
protected DataBindingContext initDataBindings() {
DataBindingContext bindingContext = new DataBindingContext();
//
IObservableValue observeTextFloatValueTextObserveWidget = WidgetProperties.text(SWT.Modify).observe(floatValueText);
// 将floatValueText和floatValue进行数据绑定
bindValue=bindingContext.bindValue(observeTextFloatValueTextObserveWidget, floatValue, null, null);
// 创建验证错误提示组件(就是Text文本框左上角的红X号,数据验证出错时显示),
ControlDecorationSupport.create(bindValue, SWT.TOP | SWT.LEFT);
return bindingContext;
}
}
实际的结果是:
当修改Text文本框内容为一个合法的浮点数时,点击”恢复初始值”按钮Text显示内容的确可以恢复到初始值0.5,
但是当输入的内容无效,不是一个数字时,点击”恢复初始值”按钮也恢复不到初始值?
这是为什么呢?难道jface有bug?
通过跟踪代码搞清楚了原因:
假设当前Text的内容是初始值0.5,然后修改Text的内容,
不论Text文本框的内容是否为有效数字,点击”恢复初始值”按钮时,floatValue.setValue(Float.valueOf(0.5f));
确实被执行了,
但区别是当输入Text文本框的内容为无效数字时,floatValue的内容并不会被修改,也就是还保持之前的值(0.5),此时再点击”恢复初始值”按钮时,设置的值还是0.5,floatValue并没有改变,所以没有触发Text的更新。
再做一个试验来验证上面的逻辑:
如果先将Text的内容从0.5改为另一个有效数字(比如0.9)—(此时floatValue被更新为0.9),然后再改为一个无效数字hello,然后点击”恢复初始值”按钮,则Text的内容正常恢复。
由此找出了问题的原因:当数据对象更新的值与原值相等时,setValue不能触发Widget组件的更新。
怎么解决呢?仔细研究了,org.eclipse.core.databinding.ValueBinding
代码中的数据更新的方法doUpdate,及其调用层次结构,由此就找到了本文开始的答案。
ValueBinding继承于抽象类Binding,Binding类中的updateModelToTarget方法,就是实现从数据对象到目标对象(比如Widget)的更新方法,只要调用这个方法就能强制让数据对象的内容同步到目标对象。
所以解决这个问题的办法很简单,如下增加一行代码即可:
// 点击“恢复初始值”按钮时,将floatValue重新设置为初始值
btnNewButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
floatValue.setValue(Float.valueOf(0.5f));
// 强制更新Text组件内容
bindValue.updateModelToTarget();
}
});
当然仔细想想上面这样修改的确是简单,但在Text已经被更新的情况下,会多一次强制更新的动作,所以如果代码写得更仔细点,应该是这样:
// 点击“恢复初始值”按钮时,将floatValue重新设置为初始值
btnNewButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// 判断floatValue是否等于初始值(0.5f),如果是的话,只做强制更新
if(floatValue.getValue()==0.5f)
bindValue.updateModelToTarget();
else// 否则就更新floatValue,Text会自动更新。
floatValue.setValue(Float.valueOf(0.5f));
}
});