0

I have the following tableView in fxml

<TableView fx:id="tableView" prefHeight="525.0" prefWidth="814.0">
  <columns>
    <TableColumn prefWidth="75.0">
      <graphic><ToggleButton fx:id="mainToggleButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" onAction="#onMainToggleButtonAction" text="Start all" /></graphic>
      <cellFactory><ToggleButtonTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" activatedText="Started" deactivatedText="Stopped"/></cellFactory>
      <cellValueFactory><PropertyValueFactory property="state"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Side">
      <cellValueFactory><PropertyValueFactory property="side"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Source">
      <cellValueFactory><PropertyValueFactory property="sourceContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Reference" editable="true">
      <cellFactory>
        <ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
          <items>
            <FXCollections fx:factory="observableArrayList">
              <String fx:value="r01" />
              <String fx:value="r02" />
            </FXCollections>
          </items>
        </ChoiceBoxTableCellFactory>
      </cellFactory>
      <cellValueFactory><PropertyValueFactory property="referenceContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Destination">
      <cellValueFactory><PropertyValueFactory property="destinationContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Margin" editable="true">
      <cellFactory><SpinnerTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="0" max="1" initialValue="0" amountToStepBy="0.025" decimalFormat="0.000"/></cellFactory>
      <cellValueFactory><PropertyValueFactory property="margin"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Bot">
      <cellValueFactory><PropertyValueFactory property="bot"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Price">
      <cellValueFactory><PropertyValueFactory property="price"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Volume">
      <cellValueFactory><PropertyValueFactory property="volume"/></cellValueFactory>
    </TableColumn>
  </columns>
  <items>
    <FXCollections fx:factory="observableArrayList">
      <GridRowModel state="false" side="BID" sourceContract="s01" referenceContract="r01" destinationContract="d01" margin="0" bot="MinMax" price="15.125" volume="0" />
      <GridRowModel state="false" side="ASK" sourceContract="s02" referenceContract="r01" destinationContract="d02" margin="0" bot="MinMax" price="15.125" volume="0" />
    </FXCollections>
  </items>
</TableView>

The ChoiceBoxTableCellFactory has a constructor which takes both named arguments from the fxml and a setter for the items element.

public class ChoiceBoxTableCellFactory<S, T> implements Callback<TableColumn<S, String>, TableCell<S, String>> {

  private ObservableList<String> items;
  private double maxHeight;
  private double maxWidth;

  public ChoiceBoxTableCellFactory() {
  }

  public ChoiceBoxTableCellFactory(
    @NamedArg("maxHeight") double maxHeight,
    @NamedArg("maxWidth") double maxWidth) {
    this.maxHeight = maxHeight;
    this.maxWidth = maxWidth;
  }

  @Override
  public TableCell<S, String> call(TableColumn<S, String> param) {
    return new TableCell<S, String>() {

      ChoiceBox<String> choiceBox = new ChoiceBox<>(getItems());

      {
        choiceBox.setMaxHeight(maxHeight);
        choiceBox.setMaxWidth(maxWidth);
        choiceBox.valueProperty().addListener((obs, oldValue, newValue) -> {
          ObservableValue<String> value = getTableColumn().getCellObservableValue(getIndex());
          if (value instanceof WritableValue) {
            ((WritableValue<String>) value).setValue(newValue);
          }
        });
      }

      @Override
      protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
          setText(null);
          setGraphic(null);
        } else {
          if (isEditing()) {
            setText(null);
            setGraphic(null);
          } else {
            choiceBox.setValue(item);
            setText(null);
            setGraphic(choiceBox);
          }
        }
      }
    };
  }

  public ObservableList<String> getItems() {
    return items;
  }

  public void setItems(ObservableList<String> items) {
    this.items = items;
  }
}

But that throws this exception

Caused by: java.lang.IllegalArgumentException: Unable to coerce [[r01, r02]] to interface javafx.collections.ObservableList.
    at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:496)
    at com.sun.javafx.fxml.builder.ProxyBuilder$Setter.invoke(ProxyBuilder.java:533)
    at com.sun.javafx.fxml.builder.ProxyBuilder.createObjectFromDefaultConstructor(ProxyBuilder.java:338)
    ... 144 more

When I remove the maxHeight and maxWidth attributes the items field is set correctly through the setter. With these attributes the items value is wrapped in an additional array. How can I achieve the desired result?

mrt181
  • 5,080
  • 8
  • 66
  • 86
  • That looks like it should work. Do you need the `setItems` method anywhere else in your code? If not, you could try removing it, and using `...`. – James_D Feb 16 '16 at 12:03
  • when I remove the `setItems` setter i get this error `Caused by: java.lang.RuntimeException: Cannot create instance of com.rwe.st.cao.gas.attila.ui.control.ChoiceBoxTableCellFactory with given set of properties: [maxHeight, items, maxWidth]` – mrt181 Feb 16 '16 at 12:26

1 Answers1

1

That looks like it should work. I tried a simpler test and the following workarounds seemed to work there:

If you don't need the setItems() method anywhere else in your code, remove it and use the read only list properties approach. I.e. remove the setItems(...) method entirely, and in the FXML do

    <ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
      <items>
        <String fx:value="r01" />
        <String fx:value="r02" />
      </items>
    </ChoiceBoxTableCellFactory>

The other way around seems to be to use an <fx:define> block to define the items, and initialize it using an attribute:

<fx:define>
    <FXCollections fx:factory="observableArrayList" fx:id="items">
        <String fx:value="r01"/>
        <String fx:value="r02"/>
    </FXCollections>
<fx:define>
<ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
    items="$items" />
James_D
  • 201,275
  • 16
  • 291
  • 322