JFace Data Binding: Understanding Master/Detail Observables

I’ve been working with JFace a lot recently in an RCP application. One of the technologies I have started working with in detail is JFace Data Binding; now that it has left ‘provisional’ status, I figure I ought to start taking advantage of it wherever I can.

JGoodies Comparisons

As I mentally parse the API, it’s interesting to see the parallels and differences between this design and the JGoodies design. As disgusting as it sounds to me, it’s been over two years since I wrote my JGoodies articles over at Javalobby (Part 1, Part2), so forgive me if I don’t do a full-on class model comparison here; I don’t have the energy for it, and JGoodies is not on my immediate radar to be revised in my memory. Suffice it to say, as best I can tell they use very similar abstraction techniques, yet sometimes achieve things in very different ways.

Obviously, part of the differences come from the differences in the Swing and SWT APIs; something which I have come to terms with much more in recent months (oh how I used to loathe SWT…).

Master/Detail in JFace

Today what I’ve been working with is the concept of Master+Detail observables. This is one of those things that I feel like developers won’t get until they hit a problem where they need it. In many ways, data binding in general fits that mold, but the concept of a master/detail observable model is definitely on the more confusing side.

The Problem

Rather than explain the purpose, it seems best suited to explain the problem - once you understand the problem, the purpose (and there-after, the solution) is nearly trivial to understand.

Consider a user-interface, where you have a list of items in some capacity (a list of users in a drop down, or a table of purchase orders, or something of that variety). In addition, you have a section of the UI for editing (or even just viewing) the details of that selection.

I often think of a mail client - you have a list of email records with trimmed down information, and when you select one, it shows up in the detail pane.

This provides the definition of Master and Detail:

  • The selection in the list is the ‘Master’ selection.
  • The various text fields and labels in the detail pane is the ‘Detail’.

A Quick Review

In general, with a binding toolset, you gain automatic bi-directional communication between your UI component and your model. This basically means that if a field changes in the model, you’re going to see it in the UI as it changes.

What a master-detail binding provides is a connection between the master view and the detail pane. In a simple sense, you may wire a UI up between a bean and the UI:

public class EMail { private String subject; // … [etc] - other properties, getters, setters, property change, yadayada. }

// … in your RCP view public void createPartControl(Composite parent) { Email email = // get from somewhere… // Subject Text field Text subject = new Text(parent, SWT.BORDER); binding.bindValue( // Capture modification events. SWTObservables.observeText(subject, SWT.Modify), BeanObservables.observeValue(email, “subject”), new UpdateValueStrategy(UpdateValueStrategy.POLICYUPDATE), new UpdateValueStrategy(UpdateValueStrategy.POLICYUPDATE)); // … [etc] Other UI setup, yadayada. }

This is a simple example of wiring a text field to a property on a bean. The real question, however, is where does the email bean come from? Furthermore, what do we do if the email which should be observed by these text field changes (as in the UI I described above)?

Applying Master-Detail

This is where the concept of master-detail applies. What I like to do is introduce an additional view-supporting model, which really represents the current state of the user activity - in this case we’ll call it the EmailListModel - but you could name it any number of things. Here is a very simple implementation:

public class EmailListModel { private List emails; private Email selectedEmail; // … [etc] getters, setters, etc. }

This bean is a higher-level representation of the activity in the UI, and is really a POJO modeling the master selection list in our e-mail app. To avoid cluttering this walkthrough, I’m going to explicitly avoid describing how you bind to a list (the emails collection). I’ll reserve that for a follow-up. Let’s just assume for the moment that we have a UI of items driven by our emails collection, and when one is selected, our EmailListModel is updated to have selectedEmail be set to the selected item.

The important part of this is the fact that, unlike our Email beans, the EmailListModel is effectively a singleton in our app, making it much easier to pass (or retrieve) from within our detail view. This then in turn lets us watch for changes to this bean, and adjust that view accordingly. Now it’s actually quite straightforward to leverage our new model to drive our detail UI. What we must do is bind to the selectedEmail property, and wire up our SWT controls accordingly:

// … in your RCP view public void createPartControl(Composite parent) { // Using as a singleton, just as an example… EmailListModel emailList = EmailListModel.getInstance(); // Subject Text field Text subject = new Text(parent, SWT.BORDER);

// Start by observing the selected email. This may be null, and can // change at any time. IObservableValue selectedEmail = BeanObservables.observeValue(emailList, “selectedEmail”);

binding.bindValue( // Capture modification events. SWTObservables.observeText(subject, SWT.Modify), // Observe a property of our observable from above. BeansObservables.observeDetailValue(Realm.getDefault(), selectedEmail, “subject”, String.class), new UpdateValueStrategy(UpdateValueStrategy.POLICYUPDATE), new UpdateValueStrategy(UpdateValueStrategy.POLICYUPDATE)); // … [etc] Other UI setup, yadayada. }

The key of that section is obviously the call to BeanObservables.observeDetailValue - this method knows it is getting another IObservableValue which should be treated like another bean, but one that could be swapped out from underneath. Now, there is a clear, decoupled communication that occurs between our master list and our detail view:

From Master to Detail:

(Master List) Email Selected -> 
    Update EmailListModel to new selection.
(EmailListModel) Selected Email Updated ->
    Notify all listeners that selected email changed.
(Detail View) Selected Email Changed ->
    Notify all detail-bound SWT controls that selected email changed.

From Detail to Master:

(Detail View) Subject of Email Changed ->
    Notify Master bean.
(Master IObservableValue) 'subject' property changed. ->
    Notify underlying bean (if available).
(Selected Email Bean) 'subject' property changed. ->
    Notify all listeners that subject property changed.
(Master List) some email bean says 'subject' property changed. ->
    Change UI controls for that email bean (as appropriate).

Ancillary

I should make a final note that master-detail changes could get fairly complex in a large application, depending on the depth and complexity of the model. The need for this sort of sub-selection really varies based on the inter-dependencies of your UI. In my experience, the depth doesn’t go much past two on any one particular component of the UI, but it is good to know that the functionality is ‘recursive’ should the need arise.