How can I disable individual items in a JavaFX ChoiceBox?
I don't want to disable the complete ChoiceBox like this:
choiceBox.setDisable(true);
How can I disable individual items in a JavaFX ChoiceBox?
I don't want to disable the complete ChoiceBox like this:
choiceBox.setDisable(true);
There is no public API for disabling items in ChoiceBox. As suggested in the comments you can use a ComboBox instead.
The solution uses an extractor on a model property (in this case a todo task completed boolean property) to determine whether an item in the combo box should be disabled.
Example Usage
No tasks are completed, and "Feed the cat" task is selected from the drop-down.
The "Complete" button has been pressed to complete the "Feed the cat" task, which remains selected but is shown disabled.
The drop-down is shown, and the disabled "Feed the cat" task is still selected.
The "Mow the lawn" task is selected. The "Feed the cat" task remains disabled and can no longer be selected again.
Example Implementation
I manually set the opacity for the disabled items. Usually, that would occur automatically, but some quirk of the ComboBox styling doesn't persist the styling for the disabled opacity without manually overriding it (from my testing).
The factory which generates the cells is configurable. I have used the standard feedback for disabled items of setting the opacity to visually indicate they are disabled. But you could provide a different implementation (e.g for a todo list, you could provide a check mark against or strike-through of completed tasks that can no longer be selected).
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.List;
public class TodoComboBoxApp extends Application {
@Override
public void start(Stage stage) throws Exception {
ObservableList<ToDoItem> todoList = FXCollections.observableList(
List.of(
new ToDoItem("Weed the garden"),
new ToDoItem("Mow the lawn"),
new ToDoItem("Feed the cat"),
new ToDoItem("Feed the fish"),
new ToDoItem("Feed the fish to the cat")
),
todoItem -> new Observable[] { todoItem.completeProperty() }
);
ComboBox<ToDoItem> taskComboBox = new ComboBox<>(todoList);
taskComboBox.getSelectionModel().select(0);
taskComboBox.setCellFactory(param -> new ToDoCell());
taskComboBox.setButtonCell(new ToDoCell());
Button completeButton = new Button("Complete");
completeButton.setOnAction(e ->
taskComboBox
.getSelectionModel()
.getSelectedItem()
.setComplete(true)
);
VBox layout = new VBox(20, completeButton, taskComboBox);
layout.setPadding(new Insets(20));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
class ToDoItem {
private final String taskName;
private final BooleanProperty complete = new SimpleBooleanProperty();
ToDoItem(String taskName) {
this.taskName = taskName;
}
public String getTaskName() {
return taskName;
}
public boolean isComplete() {
return complete.get();
}
public void setComplete(boolean complete) {
this.complete.set(complete);
}
public BooleanProperty completeProperty() {
return complete;
}
}
class ToDoCell extends ListCell<ToDoItem> {
ToDoCell() {
disabledProperty().addListener((observable, oldValue, newValue) ->
setOpacity(newValue != null && newValue ? .6 : 1)
);
}
@Override
protected void updateItem(ToDoItem item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setDisable(false);
return;
}
setText(item.getTaskName());
setDisable(item.isComplete());
System.out.println(item.getTaskName() + ", disabled? " + isDisabled());
}
}