(Re-)Binding SWT forms with WindowBuilder

(Re-)Binding SWT forms with WindowBuilder

A very common case in UI applications are forms which are bound to exchangeable model objects. For example, one might want to bind this address form in such a way that you can set a new Address object at any time with the UI reflecting that change:

Example: Address Form

The plain old binding can be created easily using WindowBuilder:

Create Binding in WindowBuilder

The resulting code might look like this:

public class AddressViewPart extends ViewPart {  private DataBindingContext bindingContext; private Address address; private Text textName;  @Override public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(3, false));  Label lblName = new Label(parent, SWT.NONE); lblName.setText(Messages.YetAnotherViewPart_lblName_text);  textName = new Text(parent, SWT.BORDER); textName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));  // ...  bindingContext = initDataBindings(); }  protected DataBindingContext initDataBindings() { DataBindingContext bindingContext = new DataBindingContext(); // IObservableValue textNameValue = WidgetProperties.text(SWT.Modify).observe(textName); IObservableValue addressNameValue = PojoProperties.value("name").observe(address); bindingContext.bindValue(textNameValue, addressNameValue, null, null); // return bindingContext; } }

The tricky part is to bind this in such a way that the model object can be exchanged at any time. There are three possible ways:

1) Dispose bindings

The easiest way is to dispose all bindings and just re-bind whenever a new model object appears:

public class AddressViewPart extends ViewPart {  // ...  protected void setAddress(Address address) { this.address = address; if (bindingContext != null) bindingContext.dispose(); bindingContext = initDataBindings(); }  // ... }

Unfortunately, WindowBuilder insists on putting the initDataBindings call in createPartControl. Also, if you are using ControlDecorationSupport you will stumble upon Bug 341713 - DataBinding ControlDecorationSupport not disposed when DataBindingContext is disposed.

2) Binding to a WritableValue

Another way is to use a detail binding to a WritableValue which can be changed at any time:

public class AddressViewPart extends ViewPart {  private WritableValue addressValue = new WritableValue();  protected void setAddress(Address address) { this.addressValue.setValue(address); }  protected DataBindingContext initDataBindings() { // ... IObservableValue addressNameValue = PojoProperties.value("name").observeDetail(addressValue); // ... } }

Such a Binding can be created in WindowBuilder:

Detail binding in WindowBuilder

3) Bean binding

Yet another way is to make the ViewPart itself a Bean with PropertyChangeSupport:

public class AddressViewPart extends ViewPart {  private PropertyChangeSupport changes = new PropertyChangeSupport(this);  private Address address;  public Address getAddress() { return address; }  public void setAddress(Address address) { changes.firePropertyChange("address", this.address, this.address = address); }  public void addPropertyChangeListener(PropertyChangeListener l) { changes.addPropertyChangeListener(l); }  public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); }  protected DataBindingContext initDataBindings() { //... IObservableValue addressNameValue = BeanProperties.value("address.name").observe(this); //... }  }

While I like this way conceptionally, in practice it has the problems of Java’s PropertyChangeSupport being cumbersome. Also, JFace Data Binding doesn’t allow to mix Beans and Pojos, so if the Address object in the example is a Pojo, one will get NoSuchMethodException: Address.addPropertyChangeListener(java.beans.PropertyChangeListener). And there seems to be no way to generate such a binding in WindowBuilder.

It's 6 am and I missed them already

Cute Kenneth, Sleepy Nathan

This picture was taken during my family's visit at Nathan's birthday. It was a great moment though i was having a bad flu. I love my family.

Tired Brain, Distractions, and Dehydration

"Man, I'm tired!". That was probably what my brain try to tell me this afternoon. But it was stil 3 pm and I have a list of to-do's waiting on my TiddlyWiki. So I took a little break and fired up the browser to visit my favourite blogs. I even watched an episode of my favourite anime at the break hour, though I know it's a bad idea. All what I get was an even more tired brain plus a pair of heavy eyes.

Then I started to think about what was wrong with my brain. After a short walk I realized several things that might cause exhausting to my brain:
1. Bad diet. I woke late this morning and missed the breakfast. Even worse, I had instant noodles for lunch.
2. Dehydration. Long running, extense task swollen me away made me forget to drink water that was even at my desk already.
3. Distractions. Several chores distracted me, some even drove me away far enough, left me wondering "What was that?!".

So tomorrow I'll try to revive my pomodoro timers to help me keep focused, though it costs 5 mins on every pmds.

Reply Header on Thunderbird

Tadi saya mendapat pertanyaan dari salah satu karyawan mengenai Thunderbird yang tidak menyertakan Timestamp pada header reply-nya. Biasanya pada saat reply Thunderbird mengutip isi mail yang kita reply dengan header:

On wrote:

Tapi ternyata ada user yang headernya hanya:

wrote:

Untuk itu, ada yang perlu dirubah di konfigurasi Thunderbirdnya:
1. Menu Edit -> Preferences
2. Tab Advanced, tab General, Klik tombol "Config Editor..."
3. Muncul window About:Config, kemudian di field "Filter" isikan: mailnews.reply_header_type
4. Maka di tabel di bawahnya akan muncul item konfigurasi untuk mailnews.reply_header_type. Agar ada timestampnya, ubah value ini menjadi 2 dengan cara double-click item tadi dan ubah value nya menjadi 2.
5. Tutup window About:Config
6. Test dengan cara me-reply salah satu mail, maka timestamp akan muncul di header reply.