Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8126798

TableView updates not working when ValueFactory returns a new Binding

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P3
    • 7u6
    • fx2.1
    • javafx

    Description

      When a cell value factory returns a newly created expression or Binding cell values will stop receiving updates at some point. It seems that the cell that requests the observable value from the value factory does not keep a strong reference to the returned value so the cell will only receive updates from the the Observable value as long as it has not been garbage collected. The code below reproduces this issue:


      import javafx.application.Application;
      import javafx.beans.property.DoubleProperty;
      import javafx.beans.property.ReadOnlyStringWrapper;
      import javafx.beans.property.SimpleDoubleProperty;
      import javafx.beans.value.ObservableValue;
      import javafx.event.ActionEvent;
      import javafx.event.EventHandler;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableColumn.CellDataFeatures;
      import javafx.scene.control.TableView;
      import javafx.scene.layout.HBox;
      import javafx.scene.layout.HBoxBuilder;
      import javafx.scene.layout.StackPane;
      import javafx.scene.layout.VBox;
      import javafx.scene.layout.VBoxBuilder;
      import javafx.stage.Stage;
      import javafx.util.Callback;
       
       
      /**
       * Reproduce cell update bug. Compile and run the class. Press the update button a
       * couple of times and note how the values in the total column update. Press the
       * GC button a couple of times and then press the update button again. Note how
       * the values in the total column do not update anymore.
       *
       * The Value Factory for the total column does not return a property that is
       * held by the Order object, it returns a Binding that is created on the fly.
       * The cell does not hold a strong reference to this object so it can be gc'd
       */
      public class CellUpdateTest2 extends Application {
       
      static class Order {
      String name;
      DoubleProperty price = new SimpleDoubleProperty();
      DoubleProperty qty = new SimpleDoubleProperty();
       
      Order(String n, double p) {
      name = n;
      price.set(p);
      qty.set(1);
      }
      }
       
      final Order items[] = {
      new Order("Item 0", 4.0),
      new Order("Item 1", 5.0),
      new Order("Item 2", 6.0),
      new Order("Item 3", 7.0),
      new Order("Item 4", 8.0),
      new Order("Item 5", 9.0),
      new Order("Item 6", 10.0),
      new Order("Item 7", 11.0)
      };
       
      @Override
      public void start(final Stage primaryStage) throws Exception {
      final Button updateButton = new Button("Update Values");
      updateButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent actionEvent) {
      for (Order i: items) {
      i.qty.set(i.qty.get() + 1);
      }
      }
      });

      final Button gcButton = new Button("System.gc()");
      gcButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent actionEvent) {
      System.gc();
      }
      });

      final TableView<Order> tv = new TableView<Order>();
      final TableColumn<Order, String> nameCol = new TableColumn<Order, String>("Item");
      final TableColumn<Order, Number> priceCol = new TableColumn<Order, Number>("Price");
      final TableColumn<Order, Number> qtyCol = new TableColumn<Order, Number>("Quantity");
      final TableColumn<Order, Number> totalCol = new TableColumn<Order, Number>("Total");

      nameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Order, String>, ObservableValue<String>>() {
      @Override
      public ObservableValue<String> call(final CellDataFeatures<Order, String> d) {
      return new ReadOnlyStringWrapper(d.getValue().name);
      }
      });

      priceCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Order, Number>, ObservableValue<Number>>() {
      @Override
      public ObservableValue<Number> call(final CellDataFeatures<Order, Number> cellData) {
      return cellData.getValue().price;
      };
      });

      qtyCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Order, Number>, ObservableValue<Number>>() {
      @Override
      public ObservableValue<Number> call(final CellDataFeatures<Order, Number> cellData) {
      return cellData.getValue().qty;
      };
      });

      totalCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Order, Number>, ObservableValue<Number>>() {
      @Override
      public ObservableValue<Number> call(final CellDataFeatures<Order, Number> cellData) {
      return cellData.getValue().price.multiply(cellData.getValue().qty);
      };
      });

      tv.getColumns().addAll(nameCol, priceCol, qtyCol, totalCol);
      tv.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
      tv.getItems().addAll(items);

      final HBox hb = HBoxBuilder.create().children(updateButton, gcButton).build();
      final VBox vb = VBoxBuilder.create().children(tv, hb).build();
      primaryStage.setScene(new Scene(vb));
      primaryStage.setHeight(200.0);
      primaryStage.show();
      }
       
      public static void main(final String args[]) {
      launch(args);
      }
      }

      Attachments

        Activity

          People

            jgiles Jonathan Giles
            aswanepoejfx Abraham Swanepoel (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:
              Imported: