4

Is there a way to bind data to a list and/or a table using the groovy swing builder bind syntax? I could only find simple examples that bind simple properties like strings and numbers to a text field, label, or button text.

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
  • Maybe i found something for you Look at [that thread](http://stackoverflow.com/questions/15052373/groovy-swingbuilder-bind-source-how-to-call-variable-name) Cheers – fredk Feb 24 '13 at 14:02

3 Answers3

2

Had a look round, and the best I could see was using GlazedLists rather than standard Swing lists

http://www.jroller.com/aalmiray/entry/glazedlists_groovy_not_your_regular

tim_yates
  • 167,322
  • 27
  • 342
  • 338
1

There is a GlazedList plugin. And this article is very helpful. The Griffon guys swear by GlazedLists.

Lee at NCR
  • 51
  • 5
0

I just did something like this--it's really not that hard to do manually. It's still a work in progress, but if it helps anyone I can give what I have. So far it binds the data in both directions (Updating the data updates the component, editing the table updates the data and sends a notification to any propertyChangeListeners of the "Row")

I used a class to define one row of a table. You create this class to define the nature of your table. It looks something like this:

class Row
{
    // allows the table to listen for changes and user code to see when the table is edited
    @Bindable
    // The name at the top of the column
    @PropName("Col 1")
    String c1
    @Bindable
    // In the annotation I set the default editable to "false", here I'll make c2 editable.
    // This annotation can (and should) be expanded to define more column properties.
    @PropName(value="Col 2", editable=true)
    String c2
}

Note that once the rest of the code is packaged up in a class, this "Row" class is the ONLY thing that needs to be created to create a new table. You create instances of this class for each row, add them to the table and you are completely done--no other gui work aside from getting the table into a frame.

This class could include quite a bit more code--I intend to have it contain a thread that polls a database and updates the bound properties, then the table should pick up the changes instantly.

In order to provide custom column properties I defined an annotation that looks like this (I plan to add more):

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PropName {
    String value();
    boolean editable() default false
}

The rest is a class that builds the table. I keep it in a separate class so it can be reused (by instantiating it with a different "Row" class)

NOTE: I butchered this as I pasted it in so it may not run without a little work (braces probably). It used to include a frame which I removed to just include the table. You need to wrap the table returned from getTable() in a frame..

public class AutoTable<T>
{   
    SwingBuilder builder // This way external code can access the table
    def values=[] // holds the "Row" objects
    PropertyChangeListener listener={repaint()} as PropertyChangeListener

    def AutoTable(Class<T> clazz)
    {
      builder = new SwingBuilder()
      builder.build{
        table(id:'table') {
          tableModel(id:'tableModel') {
            clazz.declaredFields.findAll{ 
              it.declaredAnnotations*.annotationType().contains(PropName.class)}.each {
                def annotation=it.declaredAnnotations.find{it.annotationType()==PropName.class 
              }
              String n=annotation.value()
              propertyColumn(header:n, propertyName:it.name, editable:annotation.editable())
            }
          }
          tableModel.rowsModel.value=values
        }
    }
    // Use this to get the table so it can be inserted into a container
    def getTable() {
        return builder.table
    }

    def add(T o) {
        values.add(o)
        o.addPropertyChangeListener(listener)
    }

    def remove(T o) {
        o.removePropertyChangeListener(listener)
        values.remove(o)
    }

    def repaint() {
        builder.doLater{
            builder.table.repaint();
        }
    }   
}

There is probably a way to do this without the add/remove by exposing a bindable list but it seemed like more work without a lot of benifit.

At some point I'll probably put the finished class up somewhere--if you have read this far and are still interested, reply in a comment and I'll be sure to do it sooner rather than later.

Bill K
  • 62,186
  • 18
  • 105
  • 157