Reputation: 10253
My TableView
uses a custom CellFactory
to display a ComboBox
in one column, allowing the user to select from available options. Those options are loaded after the TableView
is populated (as they can change based on the user's selections elsewhere in the scene).
In the MCVE below, I have two columns for my Item
class: Name
and Color
. Within the Color
column, I have the ComboBox
which will display the current value of the Item's itemColor
property.
You will see that the ComboBox
is not populated with a list of values yet and item "Three" has no value selected.
What I need is this:
When the user clicks on the "Load Available Colors" button, the list for the ComboBox
is created. The user can now select any of the available colors. However, if there is not already a value for the item's color, I want the first color in the ComboBoxes
to be selected automatically; so item "Three" would now show the color "Red" as being selected.
THE MCVE
Item.java:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Item {
private StringProperty itemName = new SimpleStringProperty();
private StringProperty itemColor = new SimpleStringProperty();
public Item(String name, String color) {
this.itemName.set(name);
this.itemColor.set(color);
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String itemName) {
this.itemName.set(itemName);
}
public StringProperty itemNameProperty() {
return itemName;
}
public String getItemColor() {
return itemColor.get();
}
public void setItemColor(String itemColor) {
this.itemColor.set(itemColor);
}
public StringProperty itemColorProperty() {
return itemColor;
}
}
Main.java:
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// List of items
private static ObservableList<Item> listOfItems = FXCollections.observableArrayList();
// List of available Colors. These will be selectable from the ComboBox
private static ObservableList<String> availableColors = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
private static void buildSampleData() {
availableColors.addAll("Red", "Blue", "Green", "Yellow", "Black");
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Build a list of sample data. This data is loaded from my data model and passed to the constructor
// of this editor in my real application.
listOfItems.addAll(
new Item("One", "Black"),
new Item("Two", "Black"),
new Item("Three", null),
new Item("Four", "Green"),
new Item("Five", "Red")
);
// TableView to display the list of items
TableView<Item> tableView = new TableView<>();
// Create the TableColumn
TableColumn<Item, String> colName = new TableColumn<>("Name");
TableColumn<Item, String> colColor = new TableColumn<>("Color");
// Cell Property Factories
colName.setCellValueFactory(column -> new SimpleObjectProperty<>(column.getValue().getItemName()));
colColor.setCellValueFactory(column -> new SimpleObjectProperty<>(column.getValue().getItemColor()));
// Add ComboBox to the Color column, populated with the list of availableColors
colColor.setCellFactory(tc -> {
ComboBox<String> comboBox = new ComboBox<>(availableColors);
comboBox.setMaxWidth(Double.MAX_VALUE);
TableCell<Item, String> cell = new TableCell<Item, String>() {
@Override
protected void updateItem(String color, boolean empty) {
super.updateItem(color, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(comboBox);
comboBox.setValue(color);
}
}
};
// Set the action of the ComboBox to set the right Value to the ValuePair
comboBox.setOnAction(event -> {
listOfItems.get(cell.getIndex()).setItemColor(comboBox.getValue());
});
return cell;
});
// Add the column to the TableView
tableView.getColumns().addAll(colName, colColor);
tableView.setItems(listOfItems);
// Add button to load the data
Button btnLoadData = new Button("Load Available Colors");
btnLoadData.setOnAction(event -> {
buildSampleData();
});
root.getChildren().add(btnLoadData);
// Add the TableView to the root layout
root.getChildren().add(tableView);
Button btnPrintAll = new Button("Print All");
btnPrintAll.setOnAction(event -> {
for (Item item : listOfItems) {
System.out.println(item.getItemName() + " : " + item.getItemColor());
}
});
root.getChildren().add(btnPrintAll);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
Now, with a regular ComboBox
, a simple call to comboBox.getSelectionModel().selectFirst()
after loading the availableColors
would be fine. But since this ComboBox
is created within the CellFactory
, I am not sure how to update it once the list of colors is populated.
Indidentally, I use this
CellFactory
implementation instead of aComboBoxTableCell
because I want them to be visible without having to enter edit mode on theTableView
.
Upvotes: 1
Views: 334
Reputation: 10253
I actually took kleopatra's advice and updated my data model to include a default value instead. I agree this is cleaner and more appropriate approach.
Upvotes: 1