16

Is it possible to set a value and a label to a JComboBox so I can show a label but get a value that is different?

For example in JavaScript I can do:

document.getElementById("myselect").options[0].value //accesses value attribute of 1st option
document.getElementById("myselect").options[0].text //accesses text of 1st option
xdevel2000
  • 20,780
  • 41
  • 129
  • 196

5 Answers5

30

You can put any object inside of a JComboBox. By default, it uses the toString method of the object to display a label navigate in the combo box using the keyboard. So, the best way is probably to define and use appropriate objects inside the combo :

public class ComboItem {
    private String value;
    private String label;

    public ComboItem(String value, String label) {
        this.value = value;
        this.label = label;
    }

    public String getValue() {
        return this.value;
    }

    public String getLabel() {
        return this.label;
    }

    @Override
    public String toString() {
        return label;
    }
}
Huseyin Yagli
  • 9,896
  • 6
  • 41
  • 50
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Please, can show me a full example? I don't' understand how to put this object into a JComboBox... – xdevel2000 Apr 14 '11 at 10:40
  • 2
    @xdevel2000 Just create an aray of these objects and set it to the combobox ComboItem[] items = new ComboItem[]{new ComboItem("value1", "label1"),new ComboItem("value2", "label2")}; JComboBox cb = new JComboBox(items); – Boro Apr 14 '11 at 11:23
  • 2
    @kleopatra: then how do you do when the toString of an object is "foo", and you want to make it display "bar" in the combo? Using a renderer works. But if you hit B in the combo, it doesn't select anything. And if you hit F in the combo, it selects the item rendered as "bar". Which isn't acceptable. That's why I suggest using wrapper objects having an adequate toString method. – JB Nizet Apr 15 '11 at 14:17
  • 1
    This. This should be the accepted answer. It solves the problem perfectly. Just add a toString method that returns what you want to show in the combo, then add the objects themselves to the combo. – Cam Jackson Aug 05 '11 at 02:38
  • 1
    Also see [this blogpost](http://tips4java.wordpress.com/2013/02/18/combo-box-with-hidden-data/) which confirms what JB Nizet says. So basically, use the accepted answer if you want to display ordinary Strings in your combobox, or use a custom renderer along with a custom `KeySelectionManager`. – Timmos Feb 06 '14 at 16:50
9

Here's a utility interface and class that make it easy to get a combo box to use different labels. Instead of creating a replacement ListCellRenderer (and risking it looking out of place if the look-and-feel is changed), this uses the default ListCellRenderer (whatever that may be), but swapping in your own strings as label text instead of the ones defined by toString() in your value objects.

public interface ToString {
    public String toString(Object object);
}

public final class ToStringListCellRenderer implements ListCellRenderer {
    private final ListCellRenderer originalRenderer;
    private final ToString toString;

    public ToStringListCellRenderer(final ListCellRenderer originalRenderer,
            final ToString toString) {
        this.originalRenderer = originalRenderer;
        this.toString = toString;
    }

    public Component getListCellRendererComponent(final JList list,
            final Object value, final int index, final boolean isSelected,
            final boolean cellHasFocus) {
        return originalRenderer.getListCellRendererComponent(list,
            toString.toString(value), index, isSelected, cellHasFocus);
    }

}

As you can see the ToStringListCellRenderer gets a custom string from the ToString implementation, and then passes it to the original ListCellRenderer instead of passing in the value object itself.

To use this code, do something like the following:

// Create your combo box as normal, passing in the array of values.
final JComboBox combo = new JComboBox(values);
final ToString toString = new ToString() {
    public String toString(final Object object) {
        final YourValue value = (YourValue) object;
        // Of course you'd make your own label text below.
        return "custom label text " + value.toString();
    }
};
combo.setRenderer(new ToStringListCellRenderer(
        combo.getRenderer(), toString)));

As well as using this to make custom labels, if you make a ToString implementation that creates strings based on the system Locale, you can easily internationalize the combo box without having to change anything in your value objects.

MB.
  • 7,365
  • 6
  • 42
  • 42
  • OK,but toString method from final ToString toString it's called every time when i'm over comboBox with mouse.How do i reduce the number of calls? – Blocked May 10 '14 at 11:08
  • Nice try, it almost worked but it will fail miserably if you try using it with the generic version of JComboBox. – Stelios Adamantidis Dec 06 '19 at 13:17
6

Please, can show me a full example?

Instances of Enum are particularly convenient for this, as toString() "returns the name of this enum constant, as contained in the declaration."

enter image description here

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see http://stackoverflow.com/questions/5661556 */
public class ColorCombo extends JPanel {

    private Hue hue = Hue.values()[0];

    public ColorCombo() {
        this.setPreferredSize(new Dimension(320, 240));
        this.setBackground(hue.getColor());
        final JComboBox colorBox = new JComboBox();
        for (Hue h : Hue.values()) {
            colorBox.addItem(h);
        }
        colorBox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Hue h = (Hue) colorBox.getSelectedItem();
                ColorCombo.this.setBackground(h.getColor());
            }
        });
        this.add(colorBox);
    }

    private enum Hue {

        Cyan(Color.cyan), Magenta(Color.magenta), Yellow(Color.yellow),
        Red(Color.red), Green(Color.green), Blue(Color.blue);

        private final Color color;

        private Hue(Color color) {
            this.color = color;
        }

        public Color getColor() {
            return color;
        }
    }

    private static void display() {
        JFrame f = new JFrame("Color");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new ColorCombo());
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                display();
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
1

Use ListCellRenderer to achieve what you want. Make a class that extends JLabel and implements ListCellRenderer. Set that class as a renderer in your JComboBox using setRenderer() method. Now when you access values from your jcombobox it will be of type jlabel.

Harry Joy
  • 58,650
  • 30
  • 162
  • 207
  • 1
    This will work, but will break keyboard navigation, which is based on the toString of the combo items. – JB Nizet Apr 14 '11 at 10:12
  • @JB Nizet: then the keyboard navigation is broken ;-) The way to go is to fix it instead of dirty hacks in toString – kleopatra Apr 15 '11 at 12:46
  • @kleopatra: The keyboard navigation code is deeply hidden inside a non-accessible inner class of javax.swing.plaf.basic.BasicComboUI, and there is no easy way, other than reimplementing it and coupling it to the renderer, of overriding it. I agree with your principle, but the design of this part of Swing makes it very very painful to follow this principle. I think a generic wrapper object redefining toString and delegating everything else (equals, hashCode) to the wrapped object is a better solution. – JB Nizet Apr 15 '11 at 14:26
  • hmm ... doesn't it use the combo's KeySelectionManager? – kleopatra Apr 15 '11 at 14:41
  • 1
    checked: the ui queries the combo.selectWithKey which in turn delegates to its KeySelectionManager. So the clean solution is to set a well-behaved implementation of the manager (that is one which respects the actual string rep). And just noticed: SwingX has a but there as well, forgot to install a custom manager which knows about our StringValue :-) Thanks for bringing this up! – kleopatra Apr 15 '11 at 15:08
0

Step 1 Create a class with the two properties of the JComboBox, id, name for example

public class Product {
    private int id;
    private String name;


    public Product(){

    }

    public Product(int id, String name){        
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
    //this method return the value to show in the JComboBox
    @Override
    public String toString(){
       return name;
    }

}

Step 2 In the design of the form, right click in the JComboBox and select Properties, now open the code tab and in the property Type Parameters write the name of the class, in our example it is Product.

enter image description here

Step 3 Now create a method that connects to the database with a query to generate a list of products, this method receives as a parameter a JComboBox object.

public void showProducts(JComboBox <Product> comboProduct){
    ResultSet res = null;
    try {
        Connection conn = new Connection();
        String query = "select id, name from products";
        PreparedStatement ps = conn.getConecction().prepareStatement(query);
        res = ps.executeQuery();
        while (res.next()) {
            comboProduct.addItem(new Product(res.getInt("id"), res.getString("name")));
        }
        res.close();
    } catch (SQLException e) {
        System.err.println("Error showing the products " + e.getMessage());
    }

}

Step 4 You can call the method from the form

public frm_products() {
    initComponents();

    Product product = new Product(); 

    product.showProducts(this.cbo_product);

}

Now you can access the selected id using getItemAt method

System.out.println(cbo_product.getItemAt(this.cbo_product.getSelectedIndex()).getId());
Vladimir Salguero
  • 5,609
  • 3
  • 42
  • 47