Combo-box key value pair in JavaFX 2
Asked Answered
H

3

22

I am just starting to learn JavaFX 2.
Now I am trying to build a sample application. Then I got stuck in combobox.
I did not find any reference to key value pair for combobox in JavaFX.
The combobox javadoc at http://docs.oracle.com/javafx/2/api/index.html does not say much about key value pair.

How can I create a combobox that has items which have different display value and actual value ?

Hardeman answered 22/5, 2012 at 9:52 Comment(2)
Have you had a look at the tutorial on Combo Boxes?Georgeannageorgeanne
@Georgeannageorgeanne - Yes I did. It does not mention anything about creating combo-box with key value pair, just a plain combo-box with some items in it.Hardeman
S
23

You have 2 approaches:
1. Simply override the toString() method in your datamodel class. Example:

public class Demo extends Application {

    private final ObservableList<Employee> data =
            FXCollections.observableArrayList(
            new Employee("Azamat", 2200.15),
            new Employee("Veli", 1400.0),
            new Employee("Nurbek", 900.5));

    @Override
    public void start(Stage primaryStage) {

        ComboBox<Employee> combobox = new ComboBox<>(data);
        combobox.getSelectionModel().selectFirst(); // Select first as default
        combobox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Employee>() {

            @Override
            public void changed(ObservableValue<? extends Employee> arg0, Employee arg1, Employee arg2) {
                if (arg2 != null) {
                    System.out.println("Selected employee: " + arg2.getName());
                }
            }
        });
        StackPane root = new StackPane();
        root.getChildren().add(combobox);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }

    public static class Employee {
        private String name;
        private Double salary;

        @Override
        public String toString() {
            return name + " (sal:" + salary + ")";
        }

        public Employee(String name, Double salary) {
            this.name = name;
            this.salary = salary;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Double getSalary() {
            return salary;
        }

        public void setSalary(Double salary) {
            this.salary = salary;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Note the overriden toString() of the Employee class. The combobox's key (actual value) will be the Employee instance and the display value is employee.toString() in this case.
2. Second approach is to set cellFactory property of the combobox.

combobox.setCellFactory(new Callback<ListView<Employee>, ListCell<Employee>>() {

    @Override
    public ListCell<Employee> call(ListView<Employee> arg0) {
        return new ListCell<Employee>() {

            private final Button btn;
            {
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                btn = new Button();
            }

            @Override
            protected void updateItem(Employee item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setGraphic(null);
                } else {
                    btn.setStyle(item.getSalary() > 1000 ? "-fx-base:red" : "-fx-base: green");
                    btn.setText(item.getName() + "=" + item.getSalary());
                    setGraphic(btn);
                }
            }
        };
    }
});

This approach gives more powerful control over cell rendering. You can not just format display value but also include any node (control) into cell (button in this case) and add some viewing logic (item.getSalary()?"":"") too. The actual value remains the same ie Employee instance.

Sheldon answered 22/5, 2012 at 10:56 Comment(2)
Thank you very much for your answer. It was what I was looking for. I made a small change to it so that it displays items like a normal combo-box instead of having buttons in the listHardeman
How would this look if we were using a hashmap for our key-value pair? Would it use the unwieldy public void changed(ObservableValue<? extends Map<String, String>> arg0, Map<String, String> arg1, Map<String, String> arg2) {}?Jacksnipe
M
15

There is another solution, implementing a StringConverter. It's very useful for objects:

public class Product {

    private String code;
    private String name;

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Converter implementation:

public class ProductConverter extends StringConverter<Product> {

    /** Cache of Products */
    private Map<String, Product> productMap = new HashMap<String, Product>();

    @Override
    public String toString(Product product) {
        productMap.put(product.getName(), product);
        return product.getName();
    }

    @Override
    public Product fromString(String name) {
        return productMap.get(name);
    }

}

Code in view:

    ComboBox<Product> cboProducts  = new ComboBox<Product>;
    cboProducts.setConverter(new ProductConverter());
    cboProducts.getItems().addAll(serviceManager.getProductList());

To get the value of the product, you can call getValue() method:

cboProducts.getValue()
Malonis answered 8/6, 2012 at 22:45 Comment(0)
H
0

I know that this question is old, but because I just spent hours in pointless digging on this issue it's a kind of mandatory for me to share the results. It seems that the ComboBox with overriden toString() method resolves the strings back to objects just right even if there are string duplicates in the list. The resolution must be index based and toString() is used only for presentation.

Huntsville answered 10/8, 2014 at 8:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.