Best practise for data binding a list in Eclipse RCP application
Asked Answered
I

2

7

I am having trouble understanding the databinding in my Eclipse RCP application. I just can't figure out how it's supposed to work..

Here's what I want to do

I have a View where a list of texts should be displayed. The texts are "stored" in a simple List<String> which is provided via a singleton (for demonstration purpose only ;)). So whoever uses the ListProvider will get the list of texts. When the list changes I want my View to automatically update the table contents.

Here's what I've done so far

My List provider is an Observable that will notify observers when the list changes.

In my view I create an Observer to watch the list provider and when the observer is notified I refresh my view.

Code for the ListProvider:

public class ListProvider extends Observable {

    private Stack<String> hist;
    private static ListProvider instance;

    // Singleton implementation
    public static ListProvidergetInstance() {
        if (null == instance) {
            instance = new ListProvider
        }
        return instance;
    }

    public void add(String text) {
        if (this.hist.size() == MAX_SIZE) {
            this.hist.remove(MAX_SIZE-1);
        }
        this.hist.add(0, text);
        // notify all observers
        this.setChanged();
        this.notifyObservers();
    }
}

Code for the view:

public class TestView extends ViewPart {

private TableViewer viewer;

[...]

class ViewContentProvider implements IStructuredContentProvider {
    public void inputChanged(Viewer v, Object oldInput, Object newInput) {
    }
    public void dispose() {
    }
    public Object[] getElements(Object parent) {
        return ClipboardHistory.getInstance().getList().toArray();
    }
}

public void createPartControl(Composite parent) {
    viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
    IContentProvider contentProvider = new ViewContentProvider();
    viewer.setContentProvider(contentProvider);
    viewer.setLabelProvider(new ViewLabelProvider());
    viewer.setInput(getViewSite());
    
    Observer listObserver = new Observer() {
        @Override
        public void update(Observable o, Object arg) {
            // update the table viewer
            viewer.refresh();
        }
    };
    
    ListProvider.getInstance().addObserver(listObserver);
}
}

Question

Is using Observer and Observable the "right" way to go or am I missing something? I am not sure I've grasped the concept of RCP Databinding...

Ironhanded answered 11/7, 2013 at 13:11 Comment(4)
I am not sure about the scope of the items in your list but have you considered adding a simple SWT List (help.eclipse.org/juno/…) object to your view instead of a tableviewer etc etcNarra
Also it might be better to use a ListViewer(help.eclipse.org/indigo/…) instead of a TableViewer.Narra
Thanks for the input. At the moment it's only a list but I want to add more data to other columns later. Would using a ListViewer change the way I observe the list?Ironhanded
If you will need multiple columns then you will need a TableViewer.Narra
B
6

I recommend you reading JFace Databinding and make use of ViewerSupport, ObservableListContentProvider and BeanProperties to simplfy the job.

You can also see a working example JFace Databinding Snippet: Model to TableViewer binding for a reference, where the binding is done at last with the help of ViewerSupport:

(...)
TableViewer peopleViewer = new TableViewer(committers);
ViewerSupport.bind(peopleViewer, new WritableList(viewModel.getPeople(),
     Person.class), BeanProperties.value(Person.class, "name"));

With those helpers you won't need to implement the Observer for your list, and if you want your viewer to update its labels when data is modified take a look in the same example to PropertyChangeSupport, which allows you to refresh the viewer on every change on any element of the list that you notify through it. It's important that you use BeanProperties for the binding.

Brewer answered 20/7, 2013 at 4:57 Comment(1)
This seems like a good solution. But is there some way to use validators and converters with a table viewer using this?Lamas
A
1

I don't think there's anything wrong with your approach with one possible exception. You need to make sure that the viewer is refreshed in the UI thread:

Display.getDefault().asyncExec(new Runnable() {
    @Override
    public void run() {
        viewer.refresh();
    }
});

However there are things that you can do to make the table update more efficient. In your code above it appears to only add a single item at the top of the table, so you'd be better with a call to:

viewer.insert( text, 0 );
Antipas answered 12/7, 2013 at 8:22 Comment(1)
Thanks for the note on the UI thread. As for the optimization with viewer.insert I don't think it's more efficient. I can't be sure what changes were made to my model. So I would have to check for updates/inserts/deletions. That's way more expensive than just getting the whole list again and display it. ;)Ironhanded

© 2022 - 2024 — McMap. All rights reserved.