Eclipse Yoxos Services Downloads Blogs About
Home > Blogs >

on Feb 3rd, 2009Databinding: A Custom Observable for a Widget

The introduction of the databinding framework in Eclipse 3.3 is with no doubt one of the most useful tools in the hands of the form developer. The ability to transform and validate user input in such a flexible and reusable way is a great enhancement. But where there is light, there is shadow. Sometimes there is just no IObservable available for your target or model object. This blog entry will demonstrate how easy it can be to create a custom IObservable for a DateTime widget.

The DateTime widget represents one value: a java.util.Date. This Date object is the one we want to get and set on the target (target being the UI widget). Therefore we wrap the DateTime in an IObservableValue by extending the AbstractObservableValue class. Essentially an IObservable* offers methods to get and set data, to determine the type of data and to register listeners to be notified of changes. The following code demonstrates a skeleton implementation of an IObservableValue.

public class DateTimeObservableValue extends AbstractObservableValue {

  private final DateTime dateTime;
  Listener listener = new Listener() { ... };

  public DateTimeObservableValue(final DateTime dateTime) {
    this.dateTime = dateTime;
    this.dateTime.addSelectionListener(this.listener);
  }

  @Override
  protected Object doGetValue() {
    // the utility method creates a Date from the DateTime
    return dateTimeToDate();
  }

  @Override
    protected void doSetValue(final Object value) {
      if (value instanceof Date) {
        // the utility method sets the date on the DateTime
        dateToDateTime((Date) value);
      }
  }

  @Override
    public Object getValueType() {
    return Date.class;
  }

  @Override
    public synchronized void dispose() {
    this.dateTime.removeSelectionListener(this.listener);
    super.dispose();
  }
}

The implementation details are not very special. The getValueType() method has to return the type represented by this IObservableValue (which is the type Date). The do methods set and get the Date value. Since the observable has to propagate changes in the DateTime widget as soon as they ocurre, we attach a listener on the DateTime widget to inform any registered IValueChangeListener of the event. The listener implementation looks like the following:

Listener listener = new Listener() {

  @Override
  public void handleEvent(final Event event) {
    Date newValue = dateTimeToDate();

    if (!newValue.equals(DateTimeObservableValue.this.oldValue)) {
      fireValueChange(Diffs.createValueDiff(DateTimeObservableValue.this.oldValue, newValue));
      DateTimeObservableValue.this.oldValue = newValue;
    }
  }
};

In the DateTime listener we inform any interested IValueChangeListener of our DateTimeObservableValue. In order to avoid unnecessary propagation of update events in the databinding context, we compare the last set Date in the IObservableValue with the new value. Next we create a ValueDiff from our new date value and fire the the value change event. The advantage of listening to the changes in the DateTime widget, is that we are able to fire events which are either invoked by the user changing the DateTime widget or by programmatic changes of the IObservableValues wrapped Date.

You can download the full listing of the observable class here: DateTimeObservableValue.zip

As we can see, it is quite easy to write a custom observable for any kind of widget or datastructure, represented by a single value…  So, how do you embed your data in custom observables? Any obstacles you had to overcome? Problems you faced? Share them with us. icon smile Databinding: A Custom Observable for a Widget

Share and Enjoy:

  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget
  • services sprite Databinding: A Custom Observable for a Widget

9 Responses to “Databinding: A Custom Observable for a Widget”

  1. Eric Rizzo says:

    I’m curious why you did not go all the way and show the implementations of dateToDateTime() and dateTimeToDate()? Those are significant details that beginners probably want/need to see.

  2. Moritz Post says:

    Hi Eric. That is a good point. The reason was actually to perserve space. Lengthy code quoting is generally not very well received. But You are right. The snippets might be worthwile to look at. I have also attached the full source listing to download at the end of the blog entry. And here are the two missing methods:

    	private void dateToDateTime(final Date date) {
    		if (!this.dateTime.isDisposed()) {
    			Calendar cal = Calendar.getInstance();
    			cal.setTime(date);
    			this.dateTime.setYear(cal.get(Calendar.YEAR));
    			this.dateTime.setMonth(cal.get(Calendar.MONTH));
    			this.dateTime.setDay(cal.get(Calendar.DAY_OF_MONTH));
    			this.dateTime.setHours(cal.get(Calendar.HOUR_OF_DAY));
    			this.dateTime.setMinutes(cal.get(Calendar.MINUTE));
    			this.dateTime.setSeconds(cal.get(Calendar.SECOND));
    		}
    	}
    
    	private Date dateTimeToDate() {
    		Date result = null;
    		if (!this.dateTime.isDisposed()) {
    			Calendar cal = Calendar.getInstance();
    			cal.set(Calendar.YEAR, this.dateTime.getYear());
    			cal.set(Calendar.MONTH, this.dateTime.getMonth());
    			cal.set(Calendar.DAY_OF_MONTH, this.dateTime.getDay());
    			cal.set(Calendar.HOUR_OF_DAY, this.dateTime.getHours());
    			cal.set(Calendar.MINUTE, this.dateTime.getMinutes());
    			cal.set(Calendar.SECOND, this.dateTime.getSeconds());
    			result = cal.getTime();
    		}
    		return result;
    	}
    
  3. Matthew Hall says:

    As of milestone 3.5M5 (released today) there is an easier way to write custom observables, using the new properties API.

    Properties serve two purposes:
    * They act as convenient, portable observable factories for a particular property (in this example, the selection property of a DateTime widget)
    * The observables that properties create use those property instances as strategies for accessing / mutating / listening to a particular property of an object. Thus all the main requirements for writing a compliant IObservable are handled for you–you just have to plug in the getter, setter and listener parts.

    Here is the DateTimeSelectionProperty from my own workspace:

    public class DateTimeSelectionProperty extends WidgetValueProperty {
      // Each Thread gets its own Calendar instance for thread-safety
      private static final ThreadLocal calendar =
        new ThreadLocal() {
          @Override
          protected Calendar initialValue() {
            return Calendar.getInstance();
          };
        };
    
      public DateTimeSelectionProperty() {
        super( SWT.Selection );
      }
    
      public Object getValueType() {
        return Date.class;
      }
    
      @Override
      protected Object doGetValue( Object source ) {
        DateTime dateTime = (DateTime) source;
        Calendar cal = calendar.get();
    
        cal.clear();
        cal.set( dateTime.getYear(), dateTime.getMonth(), dateTime.getDay() );
        return cal.getTime();
      }
    
      @Override
      protected void doSetValue( Object source, Object value ) {
        DateTime dateTime = (DateTime) source;
        Calendar cal = calendar.get();
    
        cal.setTime( (Date) value );
        dateTime.setYear( cal.get( Calendar.YEAR ) );
        dateTime.setMonth( cal.get( Calendar.MONTH ) );
        dateTime.setDay( cal.get( Calendar.DAY_OF_MONTH ) );
      }
    }
    

    Note that the WidgetValueProperty superclass was only made public today (too late for M5) so you will have to use the next integration build to be able to subclass it.

    Properties have methods to observe a particular property on a given source object:

    DateTime dateTime = …
    IObservableValue dateTimeSelection = new DateTimeSelectionProperty().observe(dateTime);

    One neat thing about properties is that because they are stateless, they are reusable all over the place:

    IValueProperty selectionProperty = WidgetProperties.selection();

    IObservableValue buttonSelection = selectionProperty.observe((Button) button);
    IObservableValue listSelection = selectionProperty.observe((List) list);

  4. Tillmann Seidel says:

    When writing Observables for SWT controls, there is one additional thing to take care of. In some situations it might happen that the SWT control is disposed before its Observable is (for example in an IEditorPart where the editor controls are usually disposed before the IEditorPart itself). This leads to the infamous ERROR_WIDGET_DISPOSED SWTExceptions when the Observable calls methods on the disposed control.

    The solution is to add a DisposeListener to the SWT widget which makes sure that the Observable is disposed when the widget is:

    
    	private DisposeListener disposeListener = new DisposeListener() {
    		public void widgetDisposed(DisposeEvent e) {
    			DateTimeObservableValue.this.dispose();
    		}
    	};
    
    	public DateTimeObservableValue(final DateTime dateTime) {
    		this.dateTime = dateTime;
    		this.dateTime.addListener(SWT.Selection, this.listener);
    		dateTime.addDisposeListener(disposeListener);
    	}
    

    If you look at the SWT Observables that come with the data binding framework, you’ll see that they all inherit from org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue.
    The only purpose of this class is to handle the widget disposal issue. Unfortunately it is marked as internal and hence should not be subclassed outside the framework. Hopefully it will be made public API soon.

  5. Matthew Hall says:

    Tillman: WidgetValueProperty and WidgetListProperty are public API as of 3.5M6 (due out sometime tomorrow) and they already handle the widget disposal issue as you observed.

    AbstractSWTObservableValue has been removed in favor of the property implementations.

    Note also that as of 3.5M6, DateTime is supported by SWTObservables.observeSelection() and WidgetProperties.selection(). Only the year/month/day or hour/minute/second/millisecond components of the Date are supported depending on whether the DateTime is SWT.DATE, SWT.CALENDAR or SWT.TIME. In situations where the date and time need to be selected together you can observe a DateTime(CALENDAR or DATE) and a DateTime(TIME) and combine them using DateAndTimeObservableValue.

  6. Alberto Anderick Jr says:

    I’m developing an application using RCP in Brazil.

    Thanks a lot for this help!!!!!

  7. Luís Carlos Moreira says:

    I’m developing an application using RAP, RCP and GMF in Brazil.

  8. Simon Clarkson says:

    I’m developing an application using RCP and RAP outside of Brazil

  9. I’m using the dateToDateTime and dateTimeToDate methods inside a Lotus Notes plugin. I’m seeing a strange 1 hour rolling effect, every time i open and close my dialog with a DateTime object the time moves on by 1 hour.

    Has anyone else seen this effect?

    My timezone is London.

Leave a Reply

© EclipseSource 2008 - 2011