Search code examples
listviewjavafxlistviewitem

ListView CellFactory - How to remove cells correctly?


I have a ListView that I am working to add a ContextMenu to. I have the ContextMenu working find but have another issue.

My setCellFactory code, used to setup the context menus:

lvAppetites.setCellFactory(lv -> {
    ListCell<Appetite> cell = new ListCell<>();
    ContextMenu contextMenu = new ContextMenu();

    MenuItem editAppetiteMenu = new MenuItem();

    editAppetiteMenu.textProperty().bind(Bindings.format("Edit ..."));
    editAppetiteMenu.setOnAction(event -> {
        // Code to load the editor window
        editAppetite(cell.getItem());
    });
    contextMenu.getItems().add(editAppetiteMenu);

    MenuItem deleteAppetiteMenu = new MenuItem();
    deleteAppetiteMenu.textProperty().bind(Bindings.format("Delete ..."));
    deleteAppetiteMenu.setOnAction(event -> {
        // Code to delete the appetite
    });
    contextMenu.getItems().add(deleteAppetiteMenu);

    contextMenu.getItems().add(new SeparatorMenuItem());

    MenuItem addAppetiteMenu = new MenuItem();
    addAppetiteMenu.textProperty().bind(Bindings.format("Add New ..."));
    addAppetiteMenu.setOnAction(event -> {
        // Code to delete the appetite
    });
    contextMenu.getItems().add(addAppetiteMenu);

    cell.textProperty().bind(cell.itemProperty().asString());

    // If nothing selected, remove the context menu
    cell.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
        if (isNowEmpty) {
            cell.setContextMenu(null);
        } else {
            cell.setContextMenu(contextMenu);
        }
    });
    return cell;
});

My ListView is searchable through a TextField with a listener; the listener filters the items in the ListView as the user types.

The problem now, is that as the list is filtered, any empty cells now display null.

From reading another question, I'm fairly confident that the ListView is still displaying a graphic for the removed cells. I know how to handle that within the ListView by overriding the updateItem method, but how would I handle this from within my setCellFactory method instead?

Is that even possible or will I need to refactor my entire ListView?

Thank you, as always, for all your help!


Solution

  • The problem arises from the line

    cell.textProperty().bind(cell.itemProperty().asString());
    

    When the cell is empty, the item will be null, so the binding will (I believe) evaluate to the string "null".

    Try something that tests for the cell being empty or the item being null, e.g.

    cell.textProperty().bind(Bindings
        .when(cell.emptyProperty())
        .then("")
        .otherwise(cell.itemProperty().asString()));
    

    or (thanks to @fabian for refining this version)

    cell.textProperty().bind(Bindings.createStringBinding(
        () -> Objects.toString(cell.getItem(), ""),
        cell.itemProperty()));