2

I used this code snippet to get my edit commit for my table done. UITableView - Better Editing through Binding?

My problem is that i get an

Caused by: java.lang.ClassCastException: javafx.beans.property.ReadOnlyObjectWrapper cannot be cast to javafx.beans.property.SimpleStringProperty

in this line:

     SimpleStringProperty sp = (SimpleStringProperty)ov;

i have no clue what i can do about that. I'm using just SimpleStringProperty values for my data class.

Here is the full code:

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Supermain extends Application {



    @Override
    public void start(Stage primaryStage) {

        final TableView<myTextRow> table = new TableView<>();
        table.setEditable(true);
        table.setStyle("-fx-text-wrap: true;");

        //Table columns
        TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
        clmID.setMinWidth(160);
        clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));


        TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
        clmtext.setMinWidth(160);
        clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
        clmtext.setCellFactory(new TextFieldCellFactory());

        //Add data
        final ObservableList<myTextRow> data = FXCollections.observableArrayList(
                new myTextRow(5, "Lorem"),
                new myTextRow(2, "Ipsum")
        );
        table.setItems(data);
        table.getColumns().addAll(clmID, clmtext);

        HBox hBox = new HBox();
        hBox.setSpacing(5.0);
        hBox.setPadding(new Insets(5, 5, 5, 5));

        Button btn = new Button();
        btn.setText("Get Data");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                for (myTextRow data1 : data) {
                    System.out.println("data:"+data1.getText());
                }
            }
        });

        hBox.getChildren().add(btn);


        BorderPane pane = new BorderPane();
        pane.setTop(hBox);
        pane.setCenter(table);
        primaryStage.setScene(new Scene(pane, 640, 480));
        primaryStage.show();

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    public static class TextFieldCellFactory
            implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {


        @Override
        public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
            TextFieldCell textFieldCell = new TextFieldCell();
            return textFieldCell;
        }

        public static class TextFieldCell extends TableCell<myTextRow, String> {

            private TextArea textField;
            private StringProperty boundToCurrently = null;


            public TextFieldCell() {
                String strCss;
                // Padding in Text field cell is not wanted - we want the Textfield itself to "be"
                // The cell.  Though, this is aesthetic only.  to each his own.  comment out
                // to revert back.  
                strCss = "-fx-padding: 0;";

                this.setStyle(strCss);

                textField = new TextArea();
                textField.setWrapText(true);
                textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);



                //textField.setPrefHeight(real_lines_height(textField.getText(),this.getWidth(),30,23));
                // 
                // Default style pulled from caspian.css. Used to play around with the inset background colors
                // ---trying to produce a text box without borders
                strCss = ""
                        + //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" +
                        "-fx-background-color: -fx-control-inner-background;"
                        + //"-fx-background-insets: 0, 1, 2;" +
                        "-fx-background-insets: 0;"
                        + //"-fx-background-radius: 3, 2, 2;" +
                        "-fx-background-radius: 0;"
                        + // "-fx-padding: 3 5 3 5;" +   /*Play with this value to center the text depending on cell height??*/
                        "-fx-padding: 0 0 0 0;"
                        + /*Play with this value to center the text depending on cell height??*/ //"-fx-padding: 0 0 0 0;" +
                        "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);"
                        + "-fx-cursor: text;"
                        + "";

                // Focused and hover states should be set in the CSS.  This is just a test
                // to see what happens when we set the style in code
                textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
                    @Override
                    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                        TextArea tf = (TextArea) getGraphic();
                        // System.out.println(".changed() index : "+  get_ID_from_table_index(getIndex()));

                        String strStyleGotFocus = "-fx-background-color: blue, -fx-text-box-border, -fx-control-inner-background;"
                                + "-fx-background-insets: -0.4, 1, 2;"
                                + "-fx-background-radius: 3.4, 2, 2;";
                        String strStyleLostFocus
                                = //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" +
                                "-fx-background-color: -fx-control-inner-background;"
                                + //"-fx-background-insets: 0, 1, 2;" +
                                "-fx-background-insets: 0;"
                                + //"-fx-background-radius: 3, 2, 2;" +
                                "-fx-background-radius: 0;"
                                + //"-fx-padding: 3 5 3 5;" +   /**/
                                "-fx-padding: 0 0 0 0;"
                                + /**/ //"-fx-padding: 0 0 0 0;" +
                                "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);"
                                + "-fx-cursor: text;"
                                + "";
                        if (newValue.booleanValue()) {
                            tf.setStyle(strStyleGotFocus);
                        } else {
                            tf.setStyle(strStyleLostFocus);
                        }

                        if(!newValue)
                        {                                                                          


                            System.out.println("EDITABLE???? "+isEditing());
                           System.out.println("TEXT:::: "+textField.getText());
                        //    commitEdit(textField.getText());

                        }

                    }
                });
                textField.hoverProperty().addListener(new ChangeListener<Boolean>() {

                    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {

                        TextArea tf = (TextArea) getGraphic();
                        String strStyleGotHover = "-fx-background-color: derive(blue,90%), -fx-text-box-border, derive(-fx-control-inner-background, 10%);"
                                + "-fx-background-insets: 1, 2.8, 3.8;"
                                + "-fx-background-radius: 3.4, 2, 2;";
                        String strStyleLostHover
                                = //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" +
                                "-fx-background-color: -fx-control-inner-background;"
                                + //"-fx-background-insets: 0, 1, 2;" +
                                "-fx-background-insets: 0;"
                                + //"-fx-background-radius: 3, 2, 2;" +
                                "-fx-background-radius: 0;"
                                + //"-fx-padding: 3 5 3 5;" +   /**/
                                "-fx-padding: 0 0 0 0;"
                                + "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);"
                                + "-fx-cursor: text;"
                                + "";
                        String strStyleHasFocus = "-fx-background-color: blue, -fx-text-box-border, -fx-control-inner-background;"
                                + "-fx-background-insets: -0.4, 1, 2;"
                                + "-fx-background-radius: 3.4, 2, 2;";

                        if (newValue.booleanValue()) {
                            tf.setStyle(strStyleGotHover);



                        } else if (!tf.focusedProperty().get()) {
                            tf.setStyle(strStyleLostHover);
                        } else {
                            tf.setStyle(strStyleHasFocus);
                        }

                    }
                }); 

                textField.textProperty().addListener(e -> {
                    double height = 25;
                    textField.setPrefHeight(height);
                    textField.setMaxHeight(height);


                    //System.out.println("textfield Parent: "+textField.getParent().toString()); 
                });
                textField.setStyle(strCss);
                this.setGraphic(textField);
            }

            @Override
    protected void updateItem(String item, boolean empty) {
      super.updateItem(item, empty);        
      if(!empty) {
        // Show the Text Field
        this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        // Retrieve the actual String Property that should be bound to the TextField
        // If the TextField is currently bound to a different StringProperty
        // Unbind the old property and rebind to the new one
       ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
        SimpleStringProperty sp = (SimpleStringProperty)ov;

        if(this.boundToCurrently==null) {
            this.boundToCurrently = sp;
            this.textField.textProperty().bindBidirectional(sp);
        }
        else {
            if(this.boundToCurrently != sp) {
              this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
              this.boundToCurrently = sp;
              this.textField.textProperty().bindBidirectional(this.boundToCurrently);
            }
        }
        System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
        //this.textField.setText(item);  // No longer need this!!!
      }
      else {
        this.setContentDisplay(ContentDisplay.TEXT_ONLY);
      }
    }

        }


    }

    public class myTextRow {

        private final SimpleIntegerProperty ID;

        private final SimpleStringProperty text;

        public myTextRow(int ID, String text) {

            this.ID = new SimpleIntegerProperty(ID);
            this.text = new SimpleStringProperty(text);

        }

        public void setID(int id) {
            this.ID.set(id);
        }

        public void setText(String text) {
            this.text.set(text);
        }

        public int getID() {
            return ID.get();
        }

        public String getText() {
            return text.get();
        }
    }
}
Community
  • 1
  • 1
user3776738
  • 214
  • 2
  • 10
  • 27
  • Clearly the thing you are casting (`ov`) is not a `SimpleStringProperty`, it's a `ReadOnlyObjectWrapper`. Can you edit your question and provide a [MCVE]? – James_D Apr 11 '16 at 18:27
  • i upload the full code which includes the snippet from the other stackO question. – user3776738 Apr 11 '16 at 20:48

1 Answers1

4

Your model class is missing the "property accessors". Consequently, the properties themselves cannot be used by the PropertyValueFactory. As stated in the PropertyValueFactory documentation:

An example of how to use this class is:

TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name"); 
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));   

In this example, the "firstName" string is used as a reference to an assumed firstNameProperty() method in the Person class type (which is the class type of the TableView items list). Additionally, this method must return a Property instance. If a method meeting these requirements is found, then the TableCell is populated with this ObservableValue. In addition, the TableView will automatically add an observer to the returned value, such that any changes fired will be observed by the TableView, resulting in the cell immediately updating.

If no method matching this pattern exists, there is fall-through support for attempting to call get() or is() (that is, getFirstName() or isFirstName() in the example above). If a method matching this pattern exists, the value returned from this method is wrapped in a ReadOnlyObjectWrapper and returned to the TableCell.

The last paragraph describes exactly your situation, because you have no textProperty() or iDProperty() methods defined in your model class. Hence the PropertyValueFactory creates a ReadOnlyObjectWrapper for you and returns it, instead of returning the actual property instance.

Note also you have the wrong type for the id column. The property is an IntegerProperty, which is a Property<Number>, not a Property<String>. Consequently that table column needs to be a TableColumn<myTextRow, Number>. This will make it quite a bit trickier to use the table cell implementation, as you need to convert between the String in the text field and the Integer that is the value for id.

In general though, write the class following the JavaFX Property pattern as follows:

public class myTextRow {

    private final IntegerProperty id;

    private final StringProperty text;

    public myTextRow(int ID, String text) {

        this.id = new SimpleIntegerProperty(ID);
        this.text = new SimpleStringProperty(text);

    }

    public void setId(int id) {
        this.id.set(id);
    }

    public void setText(String text) {
        this.text.set(text);
    }

    public int getId() {
        return id.get();
    }

    public String getText() {
        return text.get();
    }

    public StringProperty textProperty() {
        return text;
    }

    public IntegerProperty idProperty() {
        return id ;
    }
}

and then you can define your cell value factories as

TableColumn<myTextRow, Number> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("id"));


TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));

or, using lambda expressions (which makes the code typesafe and allows the compiler to check for the existence of the correct methods):

TableColumn<myTextRow, Number> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(cellData -> cellData.getValue().idProperty());


TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(cellData -> cellData.getValue().textProperty());
James_D
  • 201,275
  • 16
  • 291
  • 322