2

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.

Ahad Porkar
  • 1,666
  • 2
  • 33
  • 68

2 Answers2

5

While violation of java naming conventions (follow them, always!) and improper useage of PropertyValueFactory (don't use it if there is no compelling reason!) are the usual suspects, they seem not to be the reason in the OP's question.

The error message indicates that the problem occurs in a modular context:

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

Actually, the example runs just fine in a non-modular context but fails when modularized. And the error message tells us exactly what to do: open our module (or parts of it) for reflective access.

Module-info to open either the complete module or individual packages:

 // complete
 open module JavaFXTest {
   exports ourpackage;
   ...
 }

 // parts
 module JavaFXTest {
   exports ourpackage;
   opens ourpackage.model;
 }
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Thank you so much for clarification. Ive done both then one from answer and the "non-modular context" from the comment section and problem solved. thumbsup: – Ahad Porkar Jun 28 '19 at 10:08
  • 3
    It maybe worth mentioning that reflective access can also be provided to a specific module: `opens ourpackage.model to javafx.base;` – fabian Jun 28 '19 at 11:01
  • @fabian good point :) When I started digging in modular context, my first error was that the private field with fxml annotation couldn't be accessed (had both model and controller in one package) - in which case the opening would be needed for two target modules (fxml and base). Having been lazy .. – kleopatra Jun 28 '19 at 11:15
2

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 Propertyes

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...

Sunflame
  • 2,993
  • 4
  • 24
  • 48
  • 1
    Thank you so much for the walkthrough and guidance. that helps alot. only one thing in this line : userId.setCellValueFactory(data -> data.getValue().userIdProperty()); im getting "Type mismatch: cannot convert from IntegerProperty to ObservableValue" error. best regard – Ahad Porkar Jun 28 '19 at 08:46
  • hmm.. agreed, the naming is terrible (and more often than not the source of much pain ;) - but here it could be a different problem: running the example as-is in a non-modular setup works just fine while running in a modular context breaks (though with a slightly different error message than the OP `Unable to make field private javafx.scene.control.TableView fx.UserController.tableView accessible: module openfx10 does not "opens fx" to module javafx.fxml`). Any idea what might be the reason? – kleopatra Jun 28 '19 at 09:06
  • okay, could make it run by adding `opens fx` (my package name is fx) to my module.info ... looks like I have to learn more about modular setups .. ;) – kleopatra Jun 28 '19 at 09:14
  • 1
    @AhadPorkar edited the answer, add the `asString()` and it should work, i didn't observe that you have `String` in the column and not `Integer`. – Sunflame Jun 28 '19 at 09:26