javajavafxcomboboxchangelistener

Change ComboBox style if ChangeListener detects change


I've inherited some code that I'm trying to extend by changing the border color of a ComboBox based on a detected ComboBox value change. However, it seems that any changes made to the ComboBox style do not make it outside of the ChangeListener lambda.

Basically, the ComboBox border is initialized to black and I want to change to color after the user changes the value, but the color set inside the ChangeListener doesn't get applied. When I step through the code I see the style string change inside the lambda ChangeListener, but it goes back to the initial style string when I exit the lambda ChangeListener.

I've included the code I'm modifying below, with the code I'm trying to add set the ComboBox style inserted and called out. Any help would be appreciated.

public class SearchScreenController 
{
    @FXML
    private TableView<ConfigBaselineFileArtifact> searchRecordsTable;
    @FXML 
    private TableColumn<ConfigBaselineFileArtifact, String> categoryColumn = new TableColumn<>("File Category");
    @FXML 
    private TableColumn<ConfigBaselineFileArtifact, ConfigBaselineFileArtifact> subcategoryColumn = new TableColumn<>("File Subcategory")

    private void createSearchTable() 
    {
        searchRecordsTable.getColumns().clear();

        //File Category and Subcategory Columns
        categoryColumn.setCellValueFactory(new PropertyValueFactory<>("fileCategoryName"));
        subcategoryColumn.setCellValueFactory(value -> new SimpleObjectProperty(value.getValue()));

        categoryColumn.setCellFactory(param -> new TableCell<ConfigBaselineFileArtifact, String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty) {
                        ComboBox<String> cb = new ComboBox<String>(FXCollections.observableArrayList(categoryList));
                        cb.setValue(item);
                        cb.setPrefWidth(145);
                        cb.setStyle("-fx-background-color: transparent; -fx-border-color: black;");

                        cb.getSelectionModel().selectedItemProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
                            if (newValue != null) {
                                int index = getIndex();
                                // Test for case that updated combobox isn't listed with list of selected records
                                if (!searchRecordsTable.getSelectionModel().getSelectedIndices().contains(index)) {
                                    searchRecordsTable.getItems().get(index).setFileCategoryName(newValue);
                                    searchRecordsTable.getItems().get(index).setFileSubCategoryName("");
                                    DataStore.getInstance().saveConfigBaselineFileArtifact(searchRecordsTable.getItems().get(index));
                                } else {
                                    // Else if updated combobox IS within list of selected records
                                    searchRecordsTable.getSelectionModel().getSelectedItems().forEach(record -> {
                                        record.setFileCategoryName(newValue);
                                        record.setFileSubCategoryName("");
                                        DataStore.getInstance().saveConfigBaselineFileArtifact(record);
                                    });
                                }

                                // Code I'm trying to add is below
                                if (newValue.equals("")) {
                                    cb.setStyle("-fx-background-color: transparent; -fx-border-color: red;");
                                } else {
                                    cb.setStyle("-fx-background-color: transparent; -fx-border-color: green;");
                                // Code I'm trying to add is above

                                searchRecordsTable.refresh();                                    
                            }
                        });
                        setGraphic(cb);
                    }
                }
            });
        searchRecordsTable.getColumns().addAll(checkBoxColumn, fileNameColumn, artifactNameColumn, categoryColumn, subcategoryColumn, sensorNameColumn, subsystemColumn, componentColumn, dateImportedColumn, importedByColumn, labelsColumn);
        searchRecordsTable.refresh();
        searchRecordsTable.setEditable(true);
        searchRecordsTable.getSelectionModel().getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    }
}

Solution

  • I think the problem is, that the border is being reset on your searchRecordsTable.refresh(); call. It seems that the updateItem(String item, boolean empty) function is called again. You need to make sure, that you store the state anywhere else, e.g. a HashMap. Something like this:

    private Map<String, Boolean> changeMap = new HashMap<>();
    //in your selectedItem listener
    if (newValue.equals("")) {
        changeMap.remove(item);
    } else {
        changeMap.put(item, true);
    }
    //before setGraphic(cb)
    if(changeMap.contains(item)) {
        cb.setStyle("-fx-background-color: transparent; -fx-border-color: green;");
    }