This isn’t much of anything, really. More the beginings of ideas, but I was chatting with Josh Marinacci earlier about databinding in Swing, and frankly, how completely unimpressed I am by what I have seen of JSR-295. Honestly, I implemented all of the functionality I have seen there in about a week on my own. Having a whole spec that does, frankly, that little work for me doesn’t get me all hot and bothered.
I made some notes about what I consider — off the top of my head — the ultimate Swing binding framework. I don’t pretend that all the issues have been worked out, but I think it is a good starting place to think about it.
More after the bump.
Please forgive my ignorance here. I am not the Swing master by any stretch — indeed large parts of the API are completely unknown to me — but this comes from my limited experience doing basic Swing stuff as a JEE guy. I know component or container would likely be better than what I am talking about below, but I think in terms of JPanel, so that is what I am going to talk about…
First, a Bean - JPanel class binding factory. This would have 3 basic operating modes: first, convention: If you have
com.something.model.Vehicle
and
com.something.view.Vehicle,
I should be able to register with the factory a package mapping:
BindingFactory.mapPackages( com.something.model , com.something.view );
Alternately I should be able to use an annotation:
@BindingSource( class=com.something.view.Vehicle )
public class VehicleView extends JPanel{
}
Both of these would allow me to do something like:
JPanel someVehicleView = BindingFactory.getView( someVehicleInstance );
Thirdly, I should be able to specify at construct time what I want:
JPanel someVehicleView = BindingFactory.getView( someVehicleInstance );
By default properties on the view instance should map to similarly named properties on the constructed panel:
model.Vehicle {
int .year
String .make
String .model
}
view.Vehicle {
JTextBox .year
JTextBox .make
JTextBox .model
}
These should be resolved to the usual “value” property on the associate widget for standard Swing types — .value for sliders .text for textBoxes, into DocumentModel for textareas…. You should also be able to specify a formatter as an annotation:
@Formatter( class=com.something.view.formatters.YearFormatter )
public JTextBox getYear();
Additionally you should have a way to specify the binding, to avoid Swing collisions or for whatever reason
view.Vehicle{
@Bind( property=”color” )
JTextBox .vehicleColor
}
If you have a custom widget, you should be able to annotate it to specify what the “value” field is:
@DefaultValue
public Object getMyCustomDefaultValue
Mappings for most types would generally be obvious — moving String to int, Integer, Float, etc. I would add a couple of annotations to help clear up the common cases:
@DefaultValue
@EmptyIsNull
public String getValue()
sets non-primitive properties to null if the value is an empty String. You could also use @Trim to default call .trim() on the property before conversion. Stacked, with @EmptyIsNull means ” ” would be null as well.
If the property on the view class is a JPanel, when contructing/binding, it should have its only contained widget set to the result of BindingFactory.getView( theProperty ). That is, if you have:
model.Vehicle {
model.Stereo .stereo
}
and
view.Vehicle {
JPanel .stereo
}
Then the JPanel should get populate with (by default) view.Stereo, and the view.Stereo should be removed on change.
If the model property is a collection, and the view property is a JPanel, then it should be resolved and each of the items in the collection should be passed to the BindingFactory and the resultant widget added to the panel:
model.Vehicle {
List
}
view.Vehicle {
JPanel (Container?) .dealerOptions
}
Both of these should have annotations on the view layer like @HideOnNull to setDisplay(false) if the property is null or @HideOnEmpty if the collection is empty. They should also support overriding the default class for display. For instance, I might have view.Stereo, but on the Vehicle panel, I just want summary information, I would want to override the default binding with a specific class:
view.StereoSummary extendsJPanel {} ….
view.Vehicle{
@ViewClass(class= view.StereoSummary)
JPanel .stereo
}
When dealing with collections, you still have the problem of change notifications from the collections. I guess you could do this through wrapper classes, but you run into problems with custom collection types. /shrug. Ideally you would want .add and .remove to work on the JPanel objects as they work on the containers, matching the widgets based on the instance equality of whatever the @DefaultValue for the contained panels are. The big thing here is to not have to modify the model to suit the view, as models tend to be shared, autogenerated, etc.
I guess I could go on and on about other cases: ComboBoxes come to mind as something that seems particularly an issue: you can bind a combo box to a property, but you need a way to assign the list of options to it, perhaps with some kind of factory interface specified as an annotation. I would add to that an option to give a property name or a formatter to control what shows up in the combo box — writing a ton of wrappers to give the correct .toString() is annoying. I have a couple of applications where I have the same bean type but I want different properties to appear as the option text in a combo box in different contexts.


You're right that one can implement a basic Presentation Model binding framework in a coupla days (or nights). I disagree that there's no value in such a thing being standardised though.
Certainly a binding framework can be written in a day or two.
But we all have taken different approaches in some aspects of the interfaces and implementations; having a standard approach is a good thing.
genesis implements Swing binding using a concept similar to what you described, except its is more generic and there are implementations for Thinlet and SWT.
Interesting. Genesis looks interesting, but at first glance it has a few things which kind of irk me:
Looking at the short example:
https://genesis.dev.java.net/nonav/3.0-EA4/maven-site/en/binding.html
1. The "binding" is one way, perhaps only the call to new SwingBinder(this, new LoginForm()). I would expect a binding framework to allow for propertyChangeEvents.
If you had:
LoginForm login = new LoginForm();
SwingBinder binder = new SwingBinder(this, login);
bind.bind();
login.setUsername( "FOO" );
I expect nothing would happen.
2. It requires that the "Model" be altered.
@Form
public class LoginForm ...
or it looks like optionally, and perhaps better:
/**
* @Form
*/
public class LoginForm
Still, that means I have to edit the form level code, which is something I would want to explicitly never have to do. Model code is often (usually in my experience) something that is generated (via xjc/something-web-service-ish or from an ERD), and frequently shared across projects.
3. While I assume @Action methods can be separated out into something with a cleaner MVC type separation of concerns by doing something like:
UserBean user = new UserBean();
SwingBinder binder = new SwingBinder(this, user);
bind.bind();
LoginProcessBean login = new LoginProcessBean(user);
// @Actions declared here
binder = new SwingBinder(this, login);
bind.bind();
It seems a big... basic. In terms of invoking controller/logic operations, I would really rather use something better anyway (read: GUICommands).
Genesis has some interesting ideas in terms of Gui development, but I expect that actually using it, you end up doing things the way a lot of Struts developers do: creating "Form Beans" then mapping those back to the "Real" model, instead of just using the "Real" model from the beginning.
I cannot understand why relatively so little interest has been given to data binding in Java Swing applications. I have been looking around on the Internet to find a decent data binding library (preferably free) and could only find JGoodies (not free). I mean this kind of functionality has been around for years in Microsoft development tools and they do increase programmer productivity. Am I missing something?
Hi Robert,
Sorry for taking so long to reply.
1-) genesis has been conceived to avoid coding PropertyChangeListeners and all this stuff. We intended to make the framework as easy for developers as possible.
If you modify a bean in response to any view event, all changes will be detected by genesis. In our experience, it is very rare to change a bean property outside of this context, so we were not concerned about it. It is possible to update the view in these rare situations by using ActionInvoker.refresh(form), but for genesis 3.1 we intend to support it as described in issue 385.
2-)@Form is no longer needed since 3.0-EA5. However, the form class usually is a subclass of a model class, as there is usually a lot of presentation logic that belongs to the view tier.
3-)If you want to decouple actions from the model, you are going against a core principle in genesis: object-orientation. We believe data and operations belong together, as a rich domain model.
I really value your feedback, so if you can come up with a few use cases for which genesis would not be appropriate, please let me know.
Few years ago I've worked on a SWT project, and binding was the real pain of the project (excuse me for the comparison, but much like strusts forms -> model binding).
Few weeks ago I was wondering how to provide a better way, I'm starting a new pilot for it. Based on AOP + Annotations. The concept is very similar to what you've said on the post (isn't amazing how convergence works? Ppl all around the world having similar ideas for same problems).
My idea is to annotate the class, plus the properties (JTextField, Radio, CheckBox). Through AOP I would bind those values after an event is fired (must be registred as well) I'm defining transformers for the types (Component-> Double, Enum etc). Also, a RefreshPolicy Annotation to define how should the framework deal with the bound entity (discard on trigger, cache for latter use) and a Transaction annotation to deal with concurrency (optimist locking for instance). I really would love to integrate with Hibernate Validator Framework as well.
I just start coding, having plenty of ideas though.
Regards
Hi VinÃcius,
genesis works on a UI-toolkit independent way, so its programming model is quit similar for SWT as well.