0

I am trying to read from a CSV file in one class, which loops through the contents of the CSV file which takes the form of Student Number in column 1 and Question 1 to Question xAmount in the columns and creates an object from the rows in the CSV file. In another class, I am creating a table and trying to dynamically populate the table using the contents of the CSV file.

So far I have set it up so that the columns dynamically populate based on the headers in the CSV file, however whenever it comes to the rows it throws an error: "WARNING: Can not retrieve property 'student number' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@76afc271 with provided class type: class java.lang.String" and does this for each column in the CSV file. Is there anyway I can dynamically populate the rows?

Here is the code for the StudentCsv Class:

public class StudentCsv {
    private int[] marks;
    private String studentNumber;
    public StudentCsv(String studentNumber, int[] marks) {
        this.studentNumber=studentNumber;
        this.marks = marks;
    }
}

And here is the code for the tableView class:

public class tableViewTest implements Initializable {
    @FXML
    private TableView tableView;
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        ArrayList<StudentCsv> testStudents = new ArrayList<>();

        StudentCsv studentCsv1 = new StudentCsv("40236387", new int[]{2, 3, 4, 5, 6});
        StudentCsv studentCsv2 = new StudentCsv("40236388", new int[]{7, 8, 9, 10, 11});
        StudentCsv studentCsv3 = new StudentCsv("40236388", new int[]{12, 13, 14, 15, 16});

        testStudents.add(studentCsv1);
        testStudents.add(studentCsv2);
        testStudents.add(studentCsv3);

        String[] columnNames = {"Student number", "q1", "q2", "q3", "q4", "q5"};

        ObservableList<StudentCsv> studentData = FXCollections.observableArrayList();
        for (int i = 0; i < testStudents.size(); i++) {
            studentData.add(testStudents.get(i));
        }
        tableView.setItems(studentData);
        for (int i = 0; i < columnNames.length; i++) {
            TableColumn<StudentCsv, String> column = new TableColumn<>(columnNames[i]);
            column.setCellValueFactory(param ->
                    new ReadOnlyObjectWrapper<>(param.getValue()).asString());
            column.setPrefWidth(75);
            tableView.getColumns().add(column);
        }
    }
}
public class Main extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        try{
            Parent root = FXMLLoader.load(getClass().getResource("tableViewTest.fxml"));
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
        launch();
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.testfx.tableViewTest">
   <children>
      <TableView fx:id="tableView" layoutX="43.0" layoutY="76.0" prefHeight="286.0" prefWidth="505.0" />
   </children>
</AnchorPane>

When the table populates it populates like this

16
  • 1
    See if stackoverflow.com/questions/56462624/… can help. Commented Feb 9, 2023 at 15:37
  • 1
    minimal reproducible example please .. make sure to read and understand that help page, then act accordingly .. and mind the M! :) Commented Feb 9, 2023 at 16:59
  • 2
    How is it not the fix? You need to provide a callback that takes each row model instance (your StudentCsv object), and returns an ObservableValue wrapping the value to be displayed in the column. Obviously you cannot have a property called "student number", as that's not even a valid property name. Commented Feb 9, 2023 at 17:31
  • 2
    You wrote in your comment: I have provided a minimal reproducible example as requested I don't think you have. I need to be able to copy your code as is (from your question), compile it and run it without changing anything and it will display the behavior that you describe in your question. You have not posted the code of the class that extends class Application. Commented Feb 10, 2023 at 6:56
  • 2
    Your problem is the parameter you are passing to setCellValueFactory. You are returning the StudentCsv object and not one of its attributes. And the result you are seeing is probably because class StudentCsv does not override method toString. Commented Feb 10, 2023 at 7:03

1 Answer 1

2

The cell value factory for a column is a function which takes the object representing a row (a StudentCsv, which is a terrible name for the class) and returns an ObservableValue containing the value to be displayed in that column for that row (i.e. the value in each cell in the column). The function is applied to each visible row in the table to determine what to display in the cells in the column.

So your cell value factory should be a function that takes a Student and returns an ObservableValue wrapping the value for that column. The first column's cell value factory should return an ObservableValue containing the studentNumber. The remaining columns cell value factories should return an ObservableNumber containing the respective mark.

So you need to start by having accessor methods for studentNumber and marks. At a minimum:

public class Student {
    private int[] marks;
    private String studentNumber;
    public Student(String studentNumber, int[] marks) {
        this.studentNumber=studentNumber;
        this.marks = marks;
    }

    public String getStudentNumber() {
        return studentNumber;
    }

    public int[] getMarks() {
        return marks;
    }
}

Now your cell value factories can return appropriate values for each column. I fixed some of the ugly code here. I'm not sure why you have a field called studentNumber which is a String (and not a numeric type of some kind), but I left it in case there is some good reason for it.

public class TableViewTest implements Initializable {
    @FXML
    private TableView<Student> tableView;
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        ArrayList<Student> testStudents = new ArrayList<>();

        Student student1 = new Student("40236387", new int[]{2, 3, 4, 5, 6});
        Student student2 = new Student("40236388", new int[]{7, 8, 9, 10, 11});
        Student student3 = new Student("40236388", new int[]{12, 13, 14, 15, 16});

        testStudents.add(student1);
        testStudents.add(student2);
        testStudents.add(student3);

        String[] columnNames = {"Student number", "q1", "q2", "q3", "q4", "q5"};

        ObservableList<Student> studentData = FXCollections.observableArrayList();
        studentData.addAll(testStudents);
        tableView.setItems(studentData);

        TableColumn<Student, String> studentNumberColumn = new TableColumn<>("Student number");
        studentNumberColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getStudentNumber()));
        tableView.getColumns().add(studentNumberColumn);

        for (int i = 1; i < columnNames.length; i++) {
            TableColumn<Student, Number> column = new TableColumn<>(columnNames[i]);
            final int marksIndex = i-1 ;
            column.setCellValueFactory(cellData ->
                    new SimpleIntegerProperty<>(cellData.getValue().getMarks()[marksIndex]));
            column.setPrefWidth(75);
            tableView.getColumns().add(column);
        }
    }
}

This is not tested, so there may be typos.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks James, I think that would work as well. I managed to fix the problem also but in a different way. Earlier it wouldn't allow me to post an answer so I will post mine also. I appreciate you taking the time to help out :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.