0

How do I make SimpleIntegerProperty show up as blank (null)? SimpleStringProperty allows nulls to show up as blank, but SimpleIntegerProperty does not. I don't really want to use SimpleStringProperty in place of it because sorting doesn't really work properly if you view numbers as Strings...

trilogy
  • 1,738
  • 15
  • 31
  • 1
    The default value for a "null" `IntegerProperty` is 0. You would not be able to set an integer to null. You can create a custom `CellValueProperty`, however, and return a `null` cell if the value is 0. – Zephyr Oct 18 '18 at 16:58
  • 2
    See link: https://stackoverflow.com/questions/42160795/javafx-storing-null-in-a-simpleintegerproperty – Rafael Oliveira Oct 18 '18 at 17:00
  • 2
    Use a custom `cellFactory` on the appropriate `TableColumn` that sets the text to `null` when the property contains `0`. If `0` is a valid value, use some other value to mean "no data" (e.g. `-1`). – Slaw Oct 18 '18 at 17:01
  • @Zephyr no - the part that you should customize in this case is the CellFactory (_not_ CellValueFactory), as you demonstrated correctly in your answer – kleopatra Oct 19 '18 at 07:44
  • Good catch. Can't edit my comment now, though – Zephyr Oct 19 '18 at 11:17

1 Answers1

3

Here is a very simple example application to demonstrate how to do this. Basically, you will override the updateItem() method of the cell to provide an empty cell if the value of your IntegerProperty is 0.

Item.java:

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Item {
    private final StringProperty name = new SimpleStringProperty();
    private final IntegerProperty quantity = new SimpleIntegerProperty();

    public Item(String name, int qty) {
        this.name.set(name);
        this.quantity.set(qty);
    }

    public String getName() {
        return name.get();
    }

    public StringProperty nameProperty() {
        return name;
    }

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

    public int getQuantity() {
        return quantity.get();
    }

    public IntegerProperty quantityProperty() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity.set(quantity);
    }
}

Main.java:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Simple TableView
        TableView<Item> tableView = new TableView<>();
        TableColumn<Item, String> colName = new TableColumn<>("Name");
        TableColumn<Item, Number> colQuantity = new TableColumn<>("Quantity");
        // Since IntegerProperty implements ObservableValue<Number> instead of Integer, for some reason, we need
        // to change our column definition to accept that

        // Set the cell value factories
        colName.setCellValueFactory(c -> c.getValue().nameProperty());
        colQuantity.setCellValueFactory(c -> c.getValue().quantityProperty());

        // Override the CellFactory for quantity to leave empty if value is 9
        colQuantity.setCellFactory(param -> new TableCell<Item, Number>() {
            @Override
            protected void updateItem(Number itemQuantity, boolean empty) {
                super.updateItem(itemQuantity, empty);
                if (empty || itemQuantity.equals(0)) {
                    // If the item's quantity is 0, set the cell to display nothing (null)
                    setText(null);
                } else {
                    // Otherwise, the cell should display a label with the value of the item's quantity
                    setText(itemQuantity.toString());

                }
            }
        });

        // Add the columns to the TableView
        tableView.getColumns().addAll(colName, colQuantity);

        // Populate the table with sample items
        tableView.getItems().setAll(
                new Item("Tools", 3),
                new Item("Saws", 0),
                new Item("Ruler", 2)
        );

        root.getChildren().add(tableView);

        // Show the Stage
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

The Result:

screenshot

EDIT

I changed the CellValueProperty for each cell to use the Item properties instead of reflection. Since the IntegerProperty implements ObservableValue<Number> instead of ObservableValue<Integer> for some reason, I did also change the definition of colQuantity to use the Number type.

Zephyr
  • 9,885
  • 4
  • 28
  • 63
  • 1
    Thanks Zephyr, that does work well however, it seems like you don't have to set up a `Label` under `setGraphic()`; you can just call `setText()`... – trilogy Oct 18 '18 at 17:23
  • If you change that , I'll set it as an answer. – trilogy Oct 18 '18 at 18:33
  • I did edit that. Using `setGraphic()`, however, is more versatile as you can add any nodes you'd like to the cell, including entire layouts (such as an image and text). – Zephyr Oct 18 '18 at 18:42
  • unrelated nit-pick: don't use PropertyValueFactory, particularly not if your data class exposes properties .. will repeat until you do it automatically – kleopatra Oct 18 '18 at 19:14
  • @kleopatra - would you please stop telling everyone not to use something that is suggested all over SO without providing an alternative? Using a `PropertyValueFactory` is the only method I've seen on here. Would be glad to change that habit, but I have no idea how else to do it. – Zephyr Oct 18 '18 at 19:18
  • Also, the [Official Oracle tutorial on TableViews](https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm) uses the same `PropertyValueFactory` method... – Zephyr Oct 18 '18 at 19:48
  • will definitely not stop to try stop suboptimal practices - dont care for the the myriads of flies the alternative is mentioned often, the tutorial is a desaster and extremely outdated (hint: find the fundamental difference to your data class ;-) – kleopatra Oct 18 '18 at 23:34
  • @kleopatra - my point is that you are not helpful. We've had this discussion before. Telling someone they are wrong without providing any direction on how to get better does this site no favors. You have a habit of travelling to various questions and answers and being very negative toward those spending time trying to help others. Please stop that. Telling a patient their sick without providing a cure is pointless. – Zephyr Oct 18 '18 at 23:48
  • Anyway, glad I could answer the OP :) – Zephyr Oct 18 '18 at 23:49
  • you did find the difference between your data class and the tutorial's data class, didn't you ;) And you did try to find out why PropertyValueFactory isn't an overwhelmingly good idea, didn't you ;) – kleopatra Oct 19 '18 at 07:35
  • @trilogy you should provide your own callback (that's the type of the cellValueFactoryProperty) - see a recent answer for details https://stackoverflow.com/questions/52885424/how-to-declare-date-value-property-for-tableview – kleopatra Oct 19 '18 at 07:40
  • So does this increase performance because then it won't use reflection? What if your data class doesn't expose the properties? – trilogy Oct 19 '18 at 13:01
  • 2
    @trilogy Yes, the avoidance of reflection will increase performance. But there is another advantage to not using `PropertyValueFactory`—better compile time safety. The lambda approach can guarantee both that the property exists and that it is of the correct type _at compile time_. – Slaw Oct 25 '18 at 15:09
  • Per @kleopatra and Slaw's comments, I updated the code in the example to remove the `PropertyValueFactory` and reflection. I believe reflection is still needed if the data class does not expose the properties, but I'm certain others can confirm or deny that. – Zephyr Oct 25 '18 at 20:05