Im trying to populate sample javafx TableView from fxml file.
this is my controller method :
public class TestController implements Initializable {
@FXML private TableView<user> tableView;
@FXML private TableColumn<user, String> UserId;
@FXML private TableColumn<user, String> UserName;
public void initialize(URL location, ResourceBundle resources) {
UserId.setCellValueFactory(new PropertyValueFactory<user, String>("userId"));
UserName.setCellValueFactory(new PropertyValueFactory<user, String>("userName"));
tableView.getItems().setAll(parseUserList());
}
private List<user> parseUserList(){
List<user> l_u = new ArrayList<user>();
user u = new user();
u.setUserId(1);
u.setUserName("test1");
l_u.add(u);
u.setUserId(2);
u.setUserName("test2");
l_u.add(u);
u.setUserId(3);
u.setUserName("test3");
l_u.add(u);
return l_u;
}
}
and the fxml file :
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="369.0" prefWidth="505.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="viewmodel.TestController ">
<center>
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn prefWidth="75.0" text="UserId" fx:id="UserId"/>
<TableColumn prefWidth="75.0" text="UserName" fx:id="UserName"/>
</columns>
</TableView>
</center>
</BorderPane>
and finally my model :
package model;
public class user {
private int userId;
public int getUserId() { return this.userId; }
public void setUserId(int userId) { this.userId = userId; }
private String userName;
public String getUserName() { return this.userName; }
public void setUserName(String userName) { this.userName = userName; }
}
now when i try to populate it gives me this error :
Jun 28, 2019 12:17:35 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'userId' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@638db851 with provided class type: class model.user
java.lang.RuntimeException: java.lang.IllegalAccessException: module javafx.base cannot access class model.user (in module JavaFXTest) because module JavaFXTest does not open model to javafx.base
at javafx.base/com.sun.javafx.property.PropertyReference.get(PropertyReference.java:176)
Some articles on SO mentioned that ,the getter and setter property must be Started with Uppercase after get word but that didn't fix the problem.
this is a very good example for "why you should use Callback
instead of PropertyValueFactory
. At the moment I don't see any reason to use PVF instead of Callback.
Here you can see one disadvantage which is you don't really see that you did a coding mistake before you run the app.
Since PVF works with reflection, it doesn't find the appropriate fields if you don't declare them correctly. The PVF expects Property
-es as you can see in the exception
a PropertyRefference
is needed and none of the fields delcared in your user
class are Property
es
It can be used this way but you have to rewrite the user
class like:
public class User { // use java naming conventions
private IntegerProperty userId;
public int getUserId() { return this.userId.get(); }
public void setUserId(int userId) { this.userId.set(userId); }
public IntegerProperty userIdProperty() { return this.userId; }
private StringProperty userName;
public String getUserName() { return this.userName.get(); }
public void setUserName(String userName) { this.userName.set(userName); }
public StringProperty userNameProperty() {return this.userName; }
public User(int userId,String userName){
this.userId = new SimpleIntegerProperty(userId);
this.userName = new SimpleStringProperty(userName);
}
}
Now since the fields are properties the PropertyValueFactory would find them but I don't recommend using it, because as you can see it can lead to problems you din't even notice before you run it.
So instead of :
UserId.setCellValueFactory(new PropertyValueFactory<user, String>("userId"));
UserName.setCellValueFactory(new PropertyValueFactory<user, String>("userName"));
use:
// use java naming conventions (`userId` instead of `UserId`)
userId.setCellValueFactory(data -> data.getValue().userIdProperty().asString());
// same here user naming convention
userName.setCellValueFactory(data -> data.getValue().userNameProperty());
I have wrote as comments a few time, but I will mention it here again to use java naming conventions. Like User instead of user and userId instead of UserId and so on...