Using the example of the Person data model described above, a Boolean property, for example. The registered status is added, and therefore the third column of the "Registered" table is added to TableView.
Consider a code example:
//The Data Model public class Person { /* * Fields */ private StringProperty firstName; private StringProperty lastName; private BooleanProperty registered; /* * Constructors */ public Person(String firstName, String lastName, boolean registered) { this.firstName = new SimpleStringProperty(firstName); this.lastName = new SimpleStringProperty(lastName); this.registered = new SimpleBooleanProperty(registered); } public Person() { this(null, null, false); } /* * Properties */ public StringProperty firstNameProperty() { return firstName; } public String getFirstName() { return this.firstName.get(); } public void setFirstName(String value) { this.firstName.set(value); } public StringProperty lastNameProperty() { return lastName; } public String getLastName() { return this.lastName.get(); } public void setLastName(String value) { this.lastName.set(value); } public BooleanProperty registeredProperty() { return registered; } public boolean isRegistered() { return this.registered.get(); } public void setRegistered(boolean value) { this.registered.set(value); } }
//Dummy values for the data model List<Person> personList = new ArrayList<Person>(); personList.add( new Person("John", "Smith", true) ; personList.add( new Person("Jack", "Smith", false) );
TableView<Person> tblView = new TableView<Person>(); tblView.setItems( FXCollections.observableList(personList) ); TableColumn firstName_col = new TableColumn("First Name"); TableColumn lastName_col = new TableColumn("Last Name"); TableColumn registered_col = new TableColumn("Registered"); firstName.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName")); lastName.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName")); registered_col.setCellValueFactory( new Callback<CellDataFeatures<Person,Boolean>,ObservableValue<Boolean>>() {
This code is working correctly. When repeated using a data model or tblView UI component to access the Registered property, it displays the correct values ββeven when it changes (i.e., Undoes / ticks).
The original problem with trying to add a flag that is not tied to a data model has not yet been received.
Suppose another call to the Select column is added, and it just contains a check box to visually indicate that one or more rows can be selected (or selected). Repeating, this checkbox column has no meaning for the Person data model. And thus, creating a property inside the Person class to store this value is semantically unnecessary and is probably considered bad coding practice. So how is this problem resolved?
How does an arbitrary BooleanProperty (or a list of it for each person in the list of persons) bind or bind to the corresponding flag?
TableColumn select_col = new TableColumn("Select");
One solution is to create a (anonymous) inner class of the Person subclass and add a Select property. Using similar code for the Registered property and its table column to bind the Select property, it should work. As indicated above, subclassing only to solve a visual problem breaks the semantics of the data model.
A better solution might be: create an internal list of logical properties for each person in personList and link them together. So, how is the corresponding Boolean property that corresponds to each person in personList in the setCellValueFactory() method setCellValueFactory() ? One possible solution is to use the index position in personList , a boolean property list for the select column and row index. So, it comes to the row index inside setCellValueFactory(CellDataFeatures) and how is this done correctly?
Consider the code:
TableColumn<Person,Boolean> select_col = new TableColumn<Person,Boolea>("Select"); List<BooleanProperty> selectedRowList = new ArrayList<BooleanProperty>(); //This callback allows the checkbox in the column to access selectedRowList (or more //exactly, the boolean property it contains Callback<Integer,ObservableValue<Boolean>> selectedStateSelectColumn = new Callback<Integer,ObservableValue<Boolean>>() { //index in this context reference the table cell index (I believe) @Override public ObservableValue<Boolean> call(Integer index) { return selectedRowList.get(index); } } //Initialise the selectedRowList for(Person p : personList) { //initially, it starts off as false, ie unticked state selectedRowList.add( new SimpleBooleanProperty() ); } select_col.setCellValueFactory( new Callback<CellDataFeatures<Person,Boolean>,ObservableValue<Boolean>> { //retrieve the cell index and use it get boolean property in the selectedRowList @Override public ObservableValue<Boolean> call(CellDataFeatures<Person,Boolean> cdf) { TableView<Person> tblView = cdf.getTableView(); Person rowData = cdf.getValue(); int rowIndex = tblView.getItems().index( rowData ); return selectedRowList.get( rowIndex ); } } select_col.setCellFactory( CheckBoxTableCell.forTableColumn(selectedStateSelectColumn));
This snippet works for me. It just needs a reorganization to compile and run. However, the main part is correct.
This problem or situation is very common, but it took me days to implement and solve it. Hope this will serve others well.