Thursday, June 19, 2008

Binding an enum to a jComboBox with user-friendly name values (Java Swing)

Binding an enum to a jComboBox is easy enough (though, as far as I know, not possible using the GUI tools that are available with NetBeans 6.1). First, of course, define the enum:

enum Timeframe {
THIS_WEEK,
THIS_MONTH,
THIS_YEAR
}

Then link it to the combo box:

ComboBoxModel cbModel = new DefaultComboBoxModel(Timeframe.values());
timeframeComboBox.setModel(cbModel);

This works fine, except that you get the not so user-friendly "THIS_WEEK", "THIS_MONTH", etc. values displayed in the dropdown. To fix this, the easiest way is to override the "toString()" method of the enum, so the modified enum becomes:

enum Timeframe {
THIS_WEEK,
THIS_MONTH,
THIS_YEAR;

@Override
public String toString(){
String[] splitNames = name().toLowerCase().split("_");
StringBuffer fixedName = new StringBuffer();

for(int i = 0; i < splitNames.length; i++) {
String firstLetter = splitNames[i].substring(0, 1).toUpperCase(),
restOfWord = splitNames[i].substring(1),
spacer = i == splitNames.length ? "" : " ";

fixedName.append(firstLetter).append(restOfWord).append(spacer);
}

return fixedName.toString();
}
}

This code first lowercases everything, then splits it up into separate words based on the "_" character. It then loops over all the words, isolates and capitalizes the first letter, and isolates the rest of the word in a separate variable.

Next, a space is added if there are more words left to be looped over. Finally, everything is re-assembled and added to the StringBuilder variable that's returned as the "fixed" name. BTW, if anyone know of a more efficient way to do this, I'd love to hear about it. Java's regex doesn't appear to support the "L" flag or anything similar, otherwise I could have done this much more efficiently with one regex replace.

In any case, no other changes are necessary, as toString() only affects the display of the enum and not its underlying values, so you can then use code that uses the enum as usual:

private void timeframeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {                                                  
JComboBox cb = (JComboBox)evt.getSource();
Timeframe timeFrame = (Timeframe)cb.getSelectedItem();
Date now = new Date();

switch(timeFrame) {
case THIS_WEEK:
//processing code...
BTW, it's interesting that Java's "switch/case" only supports ints, though I guess it does kind of force you into using enums for this type of thing, which, a lot of the time, would be the preferred method over using plain strings. (As you're probably aware, enums underlying real values are ints).

7 comments:

Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...

This is fantastic! This is exactly what I was looking for. It is precise and clear. Great stuff man!

Ruari Mears said...

Very helpful thanks, have posted a blog entry based upon your instructions, how to incorporate into the NetBeans GUI Builder:

http://www.earlution.tv/wordpress/?p=51

Ruari Mears said...

Hi again, I've noticed a typo in your for loop arguments, should read:

for ( int i = 0; i < splitNames.length; i++ )

All the best and thanks :)

Dave Gruska said...

Ruari - I'm glad the post was helpful. I'll have to try out your method when I get a chance.

Also, I fixed the typo - thanks.

PeterVermont said...

I can't format this properly, but the code below shows a more general method to customize display of Enum values by using an optional argument to the constructor.

public enum ExampleEnum {
IMPLICIT_NULL_CONSTRUCTOR, EXPLICIT_NULL_CONSTRUCTOR(), CUSTOMIZED_NAME_CONSTRUCTOR(
"your label here");
private final String displayName;

NetworkDataTypes() {
this(null);
}

NetworkDataTypes(final String displayName) {
if (displayName == null) {
this.displayName = name(); // OPTIONAL -- if displayName is null programmatically create a display name as in blog post
} else {
this.displayName = displayName;
}
}

@Override
public String toString() {
return displayName;
}
} // end enum

Anonymous said...

I am repeat first comment:

This is fantastic! This is exactly what I was looking for. It is precise and clear. Great stuff man!