Java FX: how to update ComboTableCell on change of another - javafx-2

im stuck into some problem, need guidance !
i have a TableView that has 2 ComboBoxTableCells, my requirement is to update the list in combobox of 2nd cell on change of the first.
i have tried it the following way,no luck so far.
public class Test{
private StringProperty name;
private StringProperty city;
public Test(String name, String city){
this.name = new SimpleStringProperty(name);
this.city = new SimpleStringProperty(city);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.setValue(name);
}
public String getCity() {
return city.get();
}
public void setCity(String city) {
this.city.setValue(city);
}
public StringProperty nameProperty() {return name;}
public StringProperty cityProperty() {return city;}
}
TableView _table= new TableView();
final ObservableList list = FXCollections.observableArrayList();
list.add("name 1");
list.add("name 2");
list.add("name 3");
list.add("name 4");
final ObservableList list2 = FXCollections.observableArrayList();
list2.add("city 1");
list2.add("city 2");
list2.add("city 3");
list2.add("city 4");
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Test, String>("name"));
firstNameCol.setCellFactory(ComboBoxTableCell.forTableColumn(list));
firstNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Test, String>>() {
#Override
public void handle(CellEditEvent<Test, String> t) {
((Test) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
System.out.println(t.getTableColumn().getCellData(t.getTablePosition().getRow()));
i guess have to do something here, tried the following line to see the impact on the respective cell
list2.clear();
it updated data for the whole column i just want it to be updated for the respective cell only.
}
}
);
TableColumn lastNameCol = new TableColumn("City");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Test, String>("city"));
lastNameCol.setCellFactory(ComboBoxTableCell.forTableColumn(list2));
lastNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Test, String>>() {
#Override
public void handle(CellEditEvent<Test, String> t) {
((Test) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
_table.setEditable(true);
_table.getColumns().addAll(firstNameCol,lastNameCol);
ObservableList listItems = FXCollections.observableArrayList();
listItems.add(new Test("name 4", "city 2"));
listItems.add(new Test("name 2", "city 3"));
table.getTableView().setItems(listItems);
_table.setItems(listItems);
any help will be highly appreciated. thanks

Here's a hacky approach that I haven't tested:
Add a dummy (boolean?) property on the data items that you will use to communicate between firstNameCol and lastNameCol
In the onEditCommit handler for firstNameCol, change the value of the dummy property. Be sure it changes.
Have lastNameCol be a column for the dummy property. Register a cell factory for lastNameCol that returns a TableCell with an overriden updateItem() method (pseudo-code below)
lastNameCol.setCellFactory(new Callback() {
#Override
public TableCell call(TableColumn col) {
return new TableCell() {
#Override
public void updateItem(Boolean item, boolean empty) {
if (!empty) {
// Don't care about the value of item
// Just look up the value of firstNameCol using
// getTablePosition(), then create and populate
// a ComboBox with the appropriate items and set
// it as the graphic for this cell via this.setGraphic()
// Add handler to ComboBox control to update data item when
// selection changes
}
}
};
}
});

Related

Override TreeTableCell rendering in JavaFX

I am trying to change the rendering of a TextFieldTreeTableCell to show a string as a Hyperlink as opposed to plaintext, to no avail. It seems as though it should be doable using setSkin, but something like
setSkin((new HyperLink()).getSkin());
or
setSkin((new HyperLink(getItem())).getSkin());
does not help. Any insight on how this could be done?
What you are doing wrong
You are not using the right function to customize your cell: setSkin is is used for creating custom control skins and is generic to all kinds of controls not just cells, you should a use a cell factory instead.
You are not using the right superclass: TextFieldTreeTableCell is for creating a cell which contains a label that can be made into an editable TextField when you click on it. Such functionality is not useful when you want to "display a non-editable, clickable URL".
Approach you should use
Cells have a specific method for controlling their rendering which is preferred to the skin mechanism when working with cells. This cell specific mechanism is called a cell factory and is documented with an example in the Cell documentation.
TreeTableColumns allow you to set a cell factory on the column to control the rendering of the column cells. The relevant code for rendering a Hyperlink in a cell is below:
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
Sample Application
In the screen shot below, the user has just linked on the hyperlink for anna.black#example.com.
The sample code is a modified version of the code from the Oracle TreeTableView tutorial. The addition of a visited property to the Employee class is necessary to keep track of which items in the TreeTableView have been clicked on, so that the Hyperlink visited property can be appropriately set when the cell is updated.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.*;
public class TreeTableViewSample extends Application {
private List<Employee> employees = Arrays.asList(
new Employee("Ethan Williams", "ethan.williams#example.com"),
new Employee("Emma Jones", "emma.jones#example.com"),
new Employee("Michael Brown", "michael.brown#example.com"),
new Employee("Anna Black", "anna.black#example.com"),
new Employee("Rodger York", "roger.york#example.com"),
new Employee("Susan Collins", "susan.collins#example.com"));
private final ImageView depIcon = new ImageView (
new Image("http://icons.iconarchive.com/icons/custom-icon-design/flatastic-10/16/Bear-icon.png")
);
final TreeItem<Employee> root =
new TreeItem<>(new Employee("Sales Department", ""), depIcon);
public static void main(String[] args) {
Application.launch(TreeTableViewSample.class, args);
}
final Label sendLabel = new Label();
#Override
public void start(Stage stage) {
root.setExpanded(true);
employees.forEach((employee) -> root.getChildren().add(new TreeItem<>(employee)));
stage.setTitle("Tree Table View Sample");
final Scene scene = new Scene(new VBox(), 400, 400);
scene.setFill(Color.LIGHTGRAY);
VBox sceneRoot = (VBox) scene.getRoot();
TreeTableColumn<Employee, String> empColumn =
new TreeTableColumn<>("Employee");
empColumn.setPrefWidth(150);
empColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getName())
);
TreeTableColumn<Employee, String> emailColumn =
new TreeTableColumn<>("Email");
emailColumn.setPrefWidth(190);
emailColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getEmail())
);
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().setAll(empColumn, emailColumn);
sceneRoot.getChildren().addAll(treeTableView, sendLabel);
stage.setScene(scene);
stage.show();
}
public class Employee {
private SimpleStringProperty name;
private SimpleStringProperty email;
private SimpleBooleanProperty visited;
public SimpleStringProperty nameProperty() {
if (name == null) {
name = new SimpleStringProperty(this, "name");
}
return name;
}
public SimpleStringProperty emailProperty() {
if (email == null) {
email = new SimpleStringProperty(this, "email");
}
return email;
}
private Employee(String name, String email) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleStringProperty(email);
this.visited = new SimpleBooleanProperty(false);
}
public String getName() {
return name.get();
}
public void setName(String fName) {
name.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public void setVisited(boolean visited) {
this.visited.set(visited);
}
public boolean isVisited() {
return visited.get();
}
}
}

How to unselect a selected table row upon second click/selection in javaFX

On my JavaFX table, when I click on a row, it selects that row. Now when I click for the second time on same row which was previously selected, I want to deselect that particular row. Is it possible ? Please share some example code, if its possible.
Below piece of code worked for this requirement.
tableView.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> tableView2) {
final TableRow<Person> row = new TableRow<>();
row.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
final int index = row.getIndex();
if (index >= 0 && index < tableView.getItems().size() && tableView.getSelectionModel().isSelected(index) ) {
tableView.getSelectionModel().clearSelection();
event.consume();
}
}
});
return row;
}
});
Used the same Person class from oracle's table view example. Original answer was given by #James_D in oracle's forum.
Basically you can choose anything invalid as the index. Generally -1 is preferred
table.getSelectionModel().select(-1);
which calls the int select. Alternative:
table.getSelectionModel().select(null);
which calls the object select
if you want to see the whole code used/confirm for this
public class Main extends Application {
#SuppressWarnings("unchecked")
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
TableView<Person> table = new TableView<Person>();
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("Test Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
table.getColumns().addAll(firstNameCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
table.itemsProperty().get().add(new Person("Hans"));
table.itemsProperty().get().add(new Person("Dieter"));
((Group) scene.getRoot()).getChildren().addAll(vbox);
table.getSelectionModel().select(-1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public class Person {
final StringProperty name = new SimpleStringProperty();
Person(String name) {
this.name.set(name);
}
public StringProperty nameProperty() { return this.name; }
}
}

How to update TableView Row using javaFx

I'm trying to make some downloads and show the progress inside my table:
to do that I'm using the following classes:
public class DownloadDataTable {
private SimpleDoubleProperty progress;
private SimpleStringProperty type;
private SimpleStringProperty status;
public DownloadDataTable(double progress, String type, String status) {
this.progress = new SimpleDoubleProperty(progress);
this.type = new SimpleStringProperty(type);
this.status = new SimpleStringProperty(status);
}
public double getProgress() {
return progress.get();
}
public void setProgress(double progress) {
this.progress.set(progress);
}
public String getType() {
String retorno;
if (type==null){
retorno="";
}else{
retorno=type.get();
}
return retorno;
}
public void setType (String type) {
this.type.set(type);
}
public String getStatus(){
String retorno;
if (status==null){
retorno="";
} else{
retorno=status.get();
}
return retorno;
}
public void setStatus(String status){
this.status.set(status);
}
}
and to create the TitledPane, tableview and column tables I'm doing this:
public void addDownloadToTitledPane(DownloadContent downloadContent) {
MetaDados metaDado = downloadContent.getMetaDado();
String title = metaDado.getName();
if (title.length() > 60) {
title = title.substring(0, 57) + "...";
}
TableView downloadTable = new TableView();
TableColumn<DownloadDataTable, Double> progress = new TableColumn<>("progress");
progress.setCellFactory(new Callback<TableColumn<DownloadDataTable, Double>, TableCell<DownloadDataTable, Double>>() {
#Override
public TableCell<DownloadDataTable, Double> call(TableColumn<DownloadDataTable, Double> p) {
final ProgressBar progressBar = new ProgressBar(-1);
final TableCell cell = new TableCell<DownloadDataTable, Double>() {
#Override
protected void updateItem(Double t, boolean bln) {
super.updateItem(t, bln);
if (bln) {
setText(null);
setGraphic(null);
} else {
progressBar.setProgress(t);
progressBar.prefWidthProperty().bind(this.widthProperty());
setGraphic(progressBar);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
progress.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, Double>("progress"));
progress.setText("Progresso");
TableColumn<DownloadDataTable, String> type = new TableColumn<>("type");
type.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
type.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("type"));
type.setText("Tipo");
TableColumn<DownloadDataTable, String> status = new TableColumn<>("status");
status.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
status.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("status"));
status.setText("Status");
downloadTable.getColumns().addAll(progress, type, status);
List<PendingComponents> pendingComponents = downloadContent.getPendingComponents();
ObservableList<DownloadDataTable> data = FXCollections.observableArrayList();
for (PendingComponents pendingComponent : pendingComponents) {
String typeComponent;
if (pendingComponent.getType().equalsIgnoreCase(Constants.HTML)) {
typeComponent = "Conteúdo Principal";
} else {
typeComponent = "Pacote de Imagens";
}
data.add(new DownloadDataTable(-1, typeComponent, "Preparando download"));
}
downloadTable.setItems(data);
downloadTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TitledPane downloadPane = new TitledPane(title, downloadTable);
downloadPane.setId(metaDado.getOfflineUuid());
vBoxDownload.getChildren().add(downloadPane);
}
Until here everything seems to works fine, but when I try to recover my table and update the data, my table is not updated. I've debbuged and everything seems to work, even the data value is changed, but my table still without update. See my code:
private void publishProgress(final String msg) {
Platform.runLater(new Runnable() {
#Override
public void run() {
TitledPane titledPane = (TitledPane) controller.vBoxDownload.lookup("#"+metaDado.getOfflineUuid());
TableView table = (TableView) titledPane.getContent();
DownloadDataTable data = (DownloadDataTable) table.getItems().get(0);
data.setProgress(100);
data.setStatus(msg);
}
});
}
If I try to remove and add my row it doesn't work, but if I just add another row with the new values, I got a old row with the same value and a new row with new values. I can't figure out what am I doing wrong, someone can help me??
You shouldn't need to add/remove the row to get the table to update when the progress value changes.
The problem is that you're not making the progress property accessible to the TableView. This causes the progress.setCellValueFactory(...) call to wrap your getProgress() value in a new ObservableObjectWrapper. This allows the value to display in the TableView, but it won't notify the table when the value is changed.
Add the following to your DownloadDataTable class, and your table will update when the value changes:
public SimpleDoubleProperty progressProperty() {
return this.progress;
}
public SimpleStringProperty typeProperty() {
return this.type;
}
public SimpleStringProperty statusProperty() {
return this.status;
}

Java FX 2.2 tableview data changes as I scroll up and down the tableview

I have used a tableView in JavaFX2.2. I have a column where I have kept values which are updated when the user clicks on a button. These values are dynamically getting populated and till this part its working fine. However, when I scroll down the table to see the other values in the table, the cell data changes. Can you please suggest what I need to do to get this problem resolved?
Here is the code for the table cell that I am dynamically populating and is getting changed on scrolling down the table.
Callback<TableColumn, TableCell> cellFactoryField = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(final TableColumn param) {
final Button button = new Button("Select Field");
final TableCell cell = new TableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
// label.setText("Here");
if (empty) {
// System.out.println("table cell inside updateitem = "+item);
// setGraphic(null);
}
else
{
}
}
};
button.setOnAction(new EventHandler<ActionEvent>() {
private CheckBoxTreeItem<String> checkBoxTreeItem;
private CheckBoxTreeItem<String> nodeFieldName;
private CheckBoxTreeItem<String> nodeFieldName2;
private CheckBoxTreeItem<String> nodeFieldName3;
private Stage stage = new Stage();
#Override public void handle(ActionEvent e)
{
CheckBoxTreeItem<String> rootItem =
new CheckBoxTreeItem<String>("Tables");
rootItem.setExpanded(true);
final TreeView tree = new TreeView(rootItem);
tree.setEditable(true);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
{
checkBoxTreeItem = new CheckBoxTreeItem<String>("Sample Table" );//+ (i+1));
rootItem.getChildren().add(checkBoxTreeItem);
nodeFieldName = new CheckBoxTreeItem<String>("Field Name1");
nodeFieldName2 = new CheckBoxTreeItem<String>("Field Name2");
nodeFieldName3 = new CheckBoxTreeItem<String>("Field Name3");
checkBoxTreeItem.getChildren().addAll(nodeFieldName, nodeFieldName2, nodeFieldName3);
}
tree.setRoot(rootItem);
tree.setShowRoot(true);
StackPane root = new StackPane();
root.getChildren().add(tree);
Button selectButton = new Button("Select");
HBox hbox = new HBox();
hbox.getChildren().add(selectButton);
hbox.setAlignment(Pos.CENTER);
selectButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t) {
final ArrayList<String> selectedValues = new ArrayList<String>();
// System.out.println("Selected tree items are : ");
if(checkBoxTreeItem.isSelected())
selectedValues.add(checkBoxTreeItem.getValue());
if(nodeFieldName.isSelected())
selectedValues.add(nodeFieldName.getValue());
if(nodeFieldName2.isSelected())
selectedValues.add(nodeFieldName2.getValue());
if(nodeFieldName3.isSelected())
selectedValues.add(nodeFieldName3.getValue());
stage.hide();
for(int i = 0; i<selectedValues.size();i++)
{
if(i == selectedValues.size()-1)
selectedVals += selectedValues.get(i);
else
selectedVals += selectedValues.get(i)+",";
}
fieldNameChosen = true;
if(fieldNameChosen)
cell.setGraphic(new Label(selectedVals));
else
cell.setGraphic(button);
}
});
BorderPane borderPane = new BorderPane();
borderPane.setCenter(root);
borderPane.setBottom(hbox);
stage.setScene(new Scene(new Group(borderPane)));
stage.show();
}
});
if(!(cell.getGraphic() instanceof Label))
cell.setGraphic(button);
return cell;
}
};
fieldName.setCellFactory(cellFactoryField);
I am getting a similar problem for another field where I need to show values from another table dynamically. Below is the code I have used.
final int k = 0;
value.setCellValueFactory(new Callback<CellDataFeatures<ObservableList, String>, ObservableValue<String>>() {
int noOfDataCells = k;
public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param)
{
TableColumn column = param.getTableColumn();
int size = 0;
if(tableView1.getItems()!=null)
size = ((ObservableList) tableView1.getItems().get(0)).size();
String valueFromData = "";
if(noOfDataCells<size)
{
valueFromData = String.valueOf(((ObservableList) tableView1.getItems().get(0)).get(noOfDataCells));
}
else if(noOfDataCells == size)
{
noOfDataCells = 0;
valueFromData = String.valueOf(((ObservableList) tableView1.getItems().get(0)).get(noOfDataCells));
}
else if (noOfDataCells>size)
{
valueFromData = "";
}
noOfDataCells++;
//TODO SET THE VALUE IN THE MODEL
// ((MetaTag) column.getTableView().getItems().get(
// .getIndex())).setFieldName(selectedVals);
return new SimpleStringProperty(valueFromData);
}
});
There are next problem:
You are using table editing functionality without implementing required API
You update cells in your factory, but not a data by which Table is backed up by
Cells are being destroyed on scroll out (for performance reason) and recreated back, so all your changes become destroyed.
See next tutorial on correct approach to TableView editing: http://docs.oracle.com/javafx/2/ui_controls/table-view.htm
Also I've created a small program which uses your TreeCheckBox stage to update Table by double-click on cells. I've marked with comments most important places.
public class TableCellEditing extends Application {
private void init(Stage primaryStage) {
StackPane root = new StackPane();
primaryStage.setScene(new Scene(root, 400, 200));
// you didn't provided data which your Tables use so example will work with Person class
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("click to edit", "Smith"),
new Person("", "Johnson"),
new Person("", "Williams1"),
new Person("", "Williams2"),
new Person("", "Williams3"),
new Person("", "Williams4"),
new Person("", "Williams5"),
new Person("", "Jones"),
new Person("", "Brown"),
new Person("", "Brown2"));
TableView tableView = new TableView();
tableView.setItems(data);
// make table editable
tableView.setEditable(true);
TableColumn lastNameCol = new TableColumn();
lastNameCol.setText("Last");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
TableColumn firstNameCol = new TableColumn();
firstNameCol.setText("First");
// here you connect data list with table column
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
// here you specify that your cells are special and provide editing hooks
firstNameCol.setCellFactory(new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(final TableColumn param) {
final TableCell cell = new TableCell() {
#Override
// this method is called on editable tables by double click
public void startEdit() {
super.startEdit();
// here we create new Stage to select items from tree
new CheckBoxTreeStage(this).show();
}
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
if (isEditing()) {
setText(null);
} else {
// this is the place where we update data by chosen value
setText(getItem().toString());
setGraphic(null);
}
}
}
};
return cell;
}
});
tableView.getColumns().addAll(firstNameCol, lastNameCol);
tableView.setFocusTraversable(false);
root.getChildren().add(tableView);
}
// I've extracted your stage to a separate class for better readability
private static class CheckBoxTreeStage extends Stage {
private CheckBoxTreeItem<String> checkBoxTreeItem;
private CheckBoxTreeItem<String> nodeFieldName;
private CheckBoxTreeItem<String> nodeFieldName2;
private CheckBoxTreeItem<String> nodeFieldName3;
public CheckBoxTreeStage(final TableCell cell) {
CheckBoxTreeItem<String> rootItem =
new CheckBoxTreeItem<String>("Tables");
rootItem.setExpanded(true);
final TreeView tree = new TreeView(rootItem);
tree.setEditable(true);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
{
checkBoxTreeItem = new CheckBoxTreeItem<String>("Sample Table");//+ (i+1));
rootItem.getChildren().add(checkBoxTreeItem);
nodeFieldName = new CheckBoxTreeItem<String>("Field Name1");
nodeFieldName2 = new CheckBoxTreeItem<String>("Field Name2");
nodeFieldName3 = new CheckBoxTreeItem<String>("Field Name3");
checkBoxTreeItem.getChildren().addAll(nodeFieldName, nodeFieldName2, nodeFieldName3);
}
tree.setRoot(rootItem);
tree.setShowRoot(true);
StackPane root = new StackPane();
root.getChildren().add(tree);
Button selectButton = new Button("Select");
HBox hbox = new HBox();
hbox.getChildren().add(selectButton);
hbox.setAlignment(Pos.CENTER);
selectButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
final ArrayList<String> selectedValues = new ArrayList<String>();
// System.out.println("Selected tree items are : ");
if (checkBoxTreeItem.isSelected()) {
selectedValues.add(checkBoxTreeItem.getValue());
}
if (nodeFieldName.isSelected()) {
selectedValues.add(nodeFieldName.getValue());
}
if (nodeFieldName2.isSelected()) {
selectedValues.add(nodeFieldName2.getValue());
}
if (nodeFieldName3.isSelected()) {
selectedValues.add(nodeFieldName3.getValue());
}
hide();
String selectedVals = "";
for (int i = 0; i < selectedValues.size(); i++) {
if (i == selectedValues.size() - 1) {
selectedVals += selectedValues.get(i);
} else {
selectedVals += selectedValues.get(i) + ",";
}
}
boolean fieldNameChosen = true;
if (fieldNameChosen) {
cell.commitEdit(selectedVals);
} else {
cell.cancelEdit();
}
}
});
BorderPane borderPane = new BorderPane();
borderPane.setCenter(root);
borderPane.setBottom(hbox);
setScene(new Scene(new Group(borderPane)));
}
};
public static class Person {
private StringProperty firstName;
private StringProperty lastName;
private Person(String fName, String lName) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty lastNameProperty() {
return lastName;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFx 2.1, 2.2 TableView update issue

My application uses JPA read data into TableView then modify and display them. The table refreshed modified record under JavaFx 2.0.3. Under JavaFx 2.1, 2.2, the table wouldn't refresh the update anymore. I found other people have similar issue. My plan was to continue using 2.0.3 until someone fixes the issue under 2.1 and 2.2. Now I know it is not a bug and wouldn't be fixed. Well, I don't know how to deal with this. Following are codes are modified from sample demo to show the issue. If I add a new record or delete a old record from table, table refreshes fine. If I modify a record, the table wouldn't refreshes the change until a add, delete or sort action is taken. If I remove the modified record and add it again, table refreshes. But the modified record is put at button of table. Well, if I remove the modified record, add the same record then move the record to the original spot, the table wouldn't refresh anymore. Below is a completely code, please shine some light on this.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private TextField firtNameField = new TextField();
private TextField lastNameField = new TextField();
private TextField emailField = new TextField();
private Stage editView;
private Person fPerson;
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(535);
stage.setHeight(535);
editView = new Stage();
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setMinWidth(150);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setMinWidth(150);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
//--- create a edit button and a editPane to edit person
Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
fPerson = null;
firtNameField.setText("");
lastNameField.setText("");
emailField.setText("");
editView.show();
}
});
Button editButton = new Button("Edit");
editButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (table.getSelectionModel().getSelectedItem() != null) {
fPerson = table.getSelectionModel().getSelectedItem();
firtNameField.setText(fPerson.getFirstName());
lastNameField.setText(fPerson.getLastName());
emailField.setText(fPerson.getEmail());
editView.show();
}
}
});
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (table.getSelectionModel().getSelectedItem() != null) {
data.remove(table.getSelectionModel().getSelectedItem());
}
}
});
HBox addEditDeleteButtonBox = new HBox();
addEditDeleteButtonBox.getChildren().addAll(addButton, editButton, deleteButton);
addEditDeleteButtonBox.setAlignment(Pos.CENTER_RIGHT);
addEditDeleteButtonBox.setSpacing(3);
GridPane editPane = new GridPane();
editPane.getStyleClass().add("editView");
editPane.setPadding(new Insets(3));
editPane.setHgap(5);
editPane.setVgap(5);
Label personLbl = new Label("Person:");
editPane.add(personLbl, 0, 1);
GridPane.setHalignment(personLbl, HPos.LEFT);
firtNameField.setPrefWidth(250);
lastNameField.setPrefWidth(250);
emailField.setPrefWidth(250);
Label firstNameLabel = new Label("First Name:");
Label lastNameLabel = new Label("Last Name:");
Label emailLabel = new Label("Email:");
editPane.add(firstNameLabel, 0, 3);
editPane.add(firtNameField, 1, 3);
editPane.add(lastNameLabel, 0, 4);
editPane.add(lastNameField, 1, 4);
editPane.add(emailLabel, 0, 5);
editPane.add(emailField, 1, 5);
GridPane.setHalignment(firstNameLabel, HPos.RIGHT);
GridPane.setHalignment(lastNameLabel, HPos.RIGHT);
GridPane.setHalignment(emailLabel, HPos.RIGHT);
Button saveButton = new Button("Save");
saveButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (fPerson == null) {
fPerson = new Person(
firtNameField.getText(),
lastNameField.getText(),
emailField.getText());
data.add(fPerson);
} else {
int k = -1;
if (data.size() > 0) {
for (int i = 0; i < data.size(); i++) {
if (data.get(i) == fPerson) {
k = i;
}
}
}
fPerson.setFirstName(firtNameField.getText());
fPerson.setLastName(lastNameField.getText());
fPerson.setEmail(emailField.getText());
data.set(k, fPerson);
table.setItems(data);
// The following will work, but edited person has to be added to the button
//
// data.remove(fPerson);
// data.add(fPerson);
// add and remove refresh the table, but now move edited person to original spot,
// it failed again with the following code
// while (data.indexOf(fPerson) != k) {
// int i = data.indexOf(fPerson);
// Collections.swap(data, i, i - 1);
// }
}
editView.close();
}
});
Button cancelButton = new Button("Cancel");
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
editView.close();
}
});
HBox saveCancelButtonBox = new HBox();
saveCancelButtonBox.getChildren().addAll(saveButton, cancelButton);
saveCancelButtonBox.setAlignment(Pos.CENTER_RIGHT);
saveCancelButtonBox.setSpacing(3);
VBox editBox = new VBox();
editBox.getChildren().addAll(editPane, saveCancelButtonBox);
Scene editScene = new Scene(editBox);
editView.setTitle("Person");
editView.initStyle(StageStyle.UTILITY);
editView.initModality(Modality.APPLICATION_MODAL);
editView.setScene(editScene);
editView.close();
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table, addEditDeleteButtonBox);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
See the answer to Updating rows in Tableview. Add these getters and it will just work.
Additionally since the data is an ObservableList which is set as items to tableView, any changes to this data list will be reflected to the table.getItems() too. Namely no need to table.setItems(data) again.
I have found a simple workaround for triggering the refresh of the TableView in JavaFX 2.1 TableView refresh items. It solved the issue for me.
Add this to your code:
tableView.getColumns().get(0).setVisible(false);
tableView.getColumns().get(0).setVisible(true);
looking into the TableView.java code, there's private refresh() which just executes
getProperties().put(TableViewSkinBase.REFRESH, Boolean.TRUE);
At last, the code below worked for me(Java8). (be careful, the constant's name is not REFRESH but RECREATE)
tableView.getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
(reading javafx's code, this will force cell re-creation)
Notification-based updates of JavaFX controls typically require that the properties of the data model object backing your GUI meet the minimum definition for a JavaFX Bean.
The following exemplifies the minimum code needed in order for a JavaFX property to satisfy these requirements:
public class Client extends DB {
private IntegerProperty id = new SimpleIntegerProperty();
private StringProperty lastName = new SimpleStringProperty();
private StringProperty firstName = new SimpleStringProperty();
public final int getID() {return this.id.get(); }
void setID(int id) { this.id.set(id); }
public final IntegerProperty idProperty() { return this.id; }
public final String getLastName() { return this.lastName.get(); }
public final void setLastName(String ln) { this.lastName.set(ln); }
public final StringProperty lastNameProperty() { return this.lastName; }
public final String getFirstName() { return this.firstName.get(); }
public final void setFirstName(String fn) { this.firstName.set(fn); }
public final StringProperty firstNameProperty() { return this.firstName; }
:
:
}
Glancing over your code, it does not appear that your properties satisfy the requirements for a JavFX Bean. As such, automatic notification-based updates will not occur.
I have the same problem, and not being able to add SimpleStringProperty to the POJO's used by JPA makes this a bit problematic. But it seems to me that this should be fixable issue because I have noticed the following behavior:
In my application, clicking on a row in the table populates some text fields on the screen, that the user can then edit.
At that point the user can save the data, or create a new item with the same or changed data. If the user creates a new item, which is then inserted into the observable list that the tableview represents, the change is immediately reflected in the contents of the tableview. However if the user just saves the change the new data is not reflected in the table. To put the new data in the list I'm simply doing
trialList.set(currentIndex, tempTrial);
And here's what I think points to this as a fixable issue: if I scroll the affected row out of view on the table and then scroll it back, the 'new' value(s) are now presented.
Hopefully, this can be fixed. Sorry this isn't an answer, so to speak, but might provide some insight for a fix.
this worked for me
#FXML
private void refreshTableView()
{
firstNameCol.setVisible(false);
lastNameCol.setVisible(false);
emailCol.setVisible(false);
firstNameCol.setVisible(true);
lastNameCol.setVisible(true);
emailCol.setVisible(true);
}
I had the same problem and after some search this is my workaround. I found that if the columns are removed and then re-added the table is updated.
public static <T,U> void refreshTableView(final TableView<T> tableView, final List<TableColumn<T,U>> columns, final List<T> rows) {
if (tableView == null) {
throw new NullPointerException();
}
if (columns == null) {
throw new NullPointerException();
}
if (rows == null) {
throw new NullPointerException();
}
tableView.getColumns().clear();
tableView.getColumns().addAll(columns);
ObservableList<T> list = FXCollections.observableArrayList(rows);
tableView.setItems(list);
}
Example of usage:
refreshTableView(myTableView, Arrays.asList(col1, col2, col3), rows);

Resources