ADDITIONAL SYSTEM INFORMATION :
OS: Manjaro Linux, gnome edition.
Java: Open JDK 13.0.1
JavaFX: Open JFX 13.0.1
IDE: IntelliJ Idea
Pacakge management: Maven 3.6.2-1
Java Preview features: disabled.
CPU: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
RAM: 4GB
A DESCRIPTION OF THE PROBLEM :
I need to show a read only value in a TableColumn<S, Boolean>, I set the cell factory to a CheckBoxTableCell, then the value factory is set to new PropertyValueFactory("readonlybooleanprop") and then all checkboxes are always unselected.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create the bean class, with theese properties:
private final BooleanProperty death = new SimpleBooleanProperty(true);
private final ReadOnlyBooleanWrapper canUse = new ReadOnlyBooleanWrapper(false);
2. Create the method for canUse in this way:
public ReadOnlyBooleanProperty canUseProperty(){
return canUse.getReadOnlyProperty();
}
3. Bind values in constructor:
canUse.bind(death.not());
4. Create a form and a controller class with a tableview and the following column:
TableColumn<Bean, Boolean> readOnlyColumn;
5. Set the cell factory and value factory as follows:
readOnlyColumn.setCellValueFactory(new PropertyValueFactory<>("canUse"));
readOnlyColumn.setCellFactory(CheckBoxTableCell.forTableColumn(readOnlyColumn));
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When running the app, all the objects with value true in property death should show false in property canUse, and vice versa, but only column death is editable.
ACTUAL -
While column death works as expected, column canUse show only false values. When I use the default cell factory, it shows the boolean values as String and values are ok. So, the bug is with checkbox cells.
---------- BEGIN SOURCE ----------
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.junit.jupiter.api.Test;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
/**
* Test class to run with JUnit.
*/
class MyTest {
/**
* CountDown wich allows to wait until UI test finishes.
*/
private final CountDownLatch FLOW_CONTROL = new CountDownLatch(1);
@Test
void testCheckBoxColumn() throws InterruptedException {
//Startup JavaFX and run buildandshow.
Platform.startup(this::buildAndShowUI);
//Await to stage closing.
FLOW_CONTROL.await();
//Free resources.
Platform.exit();
}
@SuppressWarnings("unchecked")
private void buildAndShowUI() {
//Build the table view.
var table = new TableView<>(FXCollections.observableArrayList(
new MyBean(1, true),
new MyBean(2, false),
new MyBean(3, true),
new MyBean(4, true),
new MyBean(5, false),
new MyBean(6, false),
new MyBean(7, true),
new MyBean(8, true)
));
//Build an integer column
var idColumn = new TableColumn<MyBean, Integer>("ID");
idColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
idColumn.setEditable(false);
//Build a CheckBox column for the SimpleProperty.
var deathColumn = new TableColumn<MyBean, Boolean>("Is Death");
deathColumn.setCellValueFactory(new PropertyValueFactory<>("death"));
deathColumn.setCellFactory(CheckBoxTableCell.forTableColumn(deathColumn));
deathColumn.setEditable(true);
//Build a checkbox column for the readonly property bound to death.not()
var canUseColumn = new TableColumn<MyBean, Boolean>("Can Use");
canUseColumn.setCellValueFactory(new PropertyValueFactory<>("canUse"));
canUseColumn.setCellFactory(CheckBoxTableCell.forTableColumn(canUseColumn));
canUseColumn.setEditable(false);
//Build a text column to see the value at canUseColumn
var canUsetext = new TableColumn<MyBean, Boolean>("Can Use?");
canUsetext.setCellValueFactory(new PropertyValueFactory<>("canUse"));
canUsetext.setEditable(false);
//Add columns
table.getColumns().addAll(idColumn, deathColumn, canUseColumn, canUsetext);
table.setEditable(true);
//Create the scene
var scene = new Scene(table);
//Create the window and show
var stage = new Stage(StageStyle.DECORATED);
stage.setOnHidden(e -> FLOW_CONTROL.countDown());
stage.setScene(scene);
stage.show();
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!Looking at the window, canUseText shows the text for the boolean value !
!read from the canUse property. And you can see that it doesn't match with !
!the state of check boxes at the checkBox column. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
}
/**
* An hypotetical bean class.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public static class MyBean {
/**
* Autogenerated numeric ID.
*/
private final IntegerProperty id = new SimpleIntegerProperty();
/**
* If this object is death.
*/
private final BooleanProperty death = new SimpleBooleanProperty();
/**
* Flag to know if you can use this object. Of course, it's the opossite of death.
*/
private final ReadOnlyBooleanWrapper canUse = new ReadOnlyBooleanWrapper();
/**
* Constructs a new bean object.
*
* @param id autogenerated id.
* @param death value of death property.
*/
public MyBean(int id, boolean death) {
canUse.bind(this.death.not());
setId(id);
setDeath(death);
}
public final IntegerProperty idProperty() {
return id;
}
public final int getId() {
return id.get();
}
public final void setId(int value) {
id.set(value);
}
public final BooleanProperty deathProperty() {
return death;
}
public final boolean isDeath() {
return death.get();
}
public final void setDeath(boolean value) {
death.set(value);
}
public final ReadOnlyBooleanProperty canUseProperty() {
return canUse.getReadOnlyProperty();
}
public final boolean getCanUse() {
return canUse.get();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var myBean = (MyBean) o;
return Objects.equals(getId(), myBean.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The method for canUse stated in step 2 to reproduce, will return the readOnly wrapper instead of the readOnlyProperty and everything works fine.
public ReadOnlyBooleanWrapper canUseProperty(){
return canUse;
}
But this workaround is breaking the purpose of a readonly property.
FREQUENCY : always
OS: Manjaro Linux, gnome edition.
Java: Open JDK 13.0.1
JavaFX: Open JFX 13.0.1
IDE: IntelliJ Idea
Pacakge management: Maven 3.6.2-1
Java Preview features: disabled.
CPU: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
RAM: 4GB
A DESCRIPTION OF THE PROBLEM :
I need to show a read only value in a TableColumn<S, Boolean>, I set the cell factory to a CheckBoxTableCell, then the value factory is set to new PropertyValueFactory("readonlybooleanprop") and then all checkboxes are always unselected.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create the bean class, with theese properties:
private final BooleanProperty death = new SimpleBooleanProperty(true);
private final ReadOnlyBooleanWrapper canUse = new ReadOnlyBooleanWrapper(false);
2. Create the method for canUse in this way:
public ReadOnlyBooleanProperty canUseProperty(){
return canUse.getReadOnlyProperty();
}
3. Bind values in constructor:
canUse.bind(death.not());
4. Create a form and a controller class with a tableview and the following column:
TableColumn<Bean, Boolean> readOnlyColumn;
5. Set the cell factory and value factory as follows:
readOnlyColumn.setCellValueFactory(new PropertyValueFactory<>("canUse"));
readOnlyColumn.setCellFactory(CheckBoxTableCell.forTableColumn(readOnlyColumn));
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When running the app, all the objects with value true in property death should show false in property canUse, and vice versa, but only column death is editable.
ACTUAL -
While column death works as expected, column canUse show only false values. When I use the default cell factory, it shows the boolean values as String and values are ok. So, the bug is with checkbox cells.
---------- BEGIN SOURCE ----------
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.junit.jupiter.api.Test;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
/**
* Test class to run with JUnit.
*/
class MyTest {
/**
* CountDown wich allows to wait until UI test finishes.
*/
private final CountDownLatch FLOW_CONTROL = new CountDownLatch(1);
@Test
void testCheckBoxColumn() throws InterruptedException {
//Startup JavaFX and run buildandshow.
Platform.startup(this::buildAndShowUI);
//Await to stage closing.
FLOW_CONTROL.await();
//Free resources.
Platform.exit();
}
@SuppressWarnings("unchecked")
private void buildAndShowUI() {
//Build the table view.
var table = new TableView<>(FXCollections.observableArrayList(
new MyBean(1, true),
new MyBean(2, false),
new MyBean(3, true),
new MyBean(4, true),
new MyBean(5, false),
new MyBean(6, false),
new MyBean(7, true),
new MyBean(8, true)
));
//Build an integer column
var idColumn = new TableColumn<MyBean, Integer>("ID");
idColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
idColumn.setEditable(false);
//Build a CheckBox column for the SimpleProperty.
var deathColumn = new TableColumn<MyBean, Boolean>("Is Death");
deathColumn.setCellValueFactory(new PropertyValueFactory<>("death"));
deathColumn.setCellFactory(CheckBoxTableCell.forTableColumn(deathColumn));
deathColumn.setEditable(true);
//Build a checkbox column for the readonly property bound to death.not()
var canUseColumn = new TableColumn<MyBean, Boolean>("Can Use");
canUseColumn.setCellValueFactory(new PropertyValueFactory<>("canUse"));
canUseColumn.setCellFactory(CheckBoxTableCell.forTableColumn(canUseColumn));
canUseColumn.setEditable(false);
//Build a text column to see the value at canUseColumn
var canUsetext = new TableColumn<MyBean, Boolean>("Can Use?");
canUsetext.setCellValueFactory(new PropertyValueFactory<>("canUse"));
canUsetext.setEditable(false);
//Add columns
table.getColumns().addAll(idColumn, deathColumn, canUseColumn, canUsetext);
table.setEditable(true);
//Create the scene
var scene = new Scene(table);
//Create the window and show
var stage = new Stage(StageStyle.DECORATED);
stage.setOnHidden(e -> FLOW_CONTROL.countDown());
stage.setScene(scene);
stage.show();
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!Looking at the window, canUseText shows the text for the boolean value !
!read from the canUse property. And you can see that it doesn't match with !
!the state of check boxes at the checkBox column. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
}
/**
* An hypotetical bean class.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public static class MyBean {
/**
* Autogenerated numeric ID.
*/
private final IntegerProperty id = new SimpleIntegerProperty();
/**
* If this object is death.
*/
private final BooleanProperty death = new SimpleBooleanProperty();
/**
* Flag to know if you can use this object. Of course, it's the opossite of death.
*/
private final ReadOnlyBooleanWrapper canUse = new ReadOnlyBooleanWrapper();
/**
* Constructs a new bean object.
*
* @param id autogenerated id.
* @param death value of death property.
*/
public MyBean(int id, boolean death) {
canUse.bind(this.death.not());
setId(id);
setDeath(death);
}
public final IntegerProperty idProperty() {
return id;
}
public final int getId() {
return id.get();
}
public final void setId(int value) {
id.set(value);
}
public final BooleanProperty deathProperty() {
return death;
}
public final boolean isDeath() {
return death.get();
}
public final void setDeath(boolean value) {
death.set(value);
}
public final ReadOnlyBooleanProperty canUseProperty() {
return canUse.getReadOnlyProperty();
}
public final boolean getCanUse() {
return canUse.get();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var myBean = (MyBean) o;
return Objects.equals(getId(), myBean.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The method for canUse stated in step 2 to reproduce, will return the readOnly wrapper instead of the readOnlyProperty and everything works fine.
public ReadOnlyBooleanWrapper canUseProperty(){
return canUse;
}
But this workaround is breaking the purpose of a readonly property.
FREQUENCY : always