JavaFX: Difference between action event triggered via context menu and accelerators - javafx

I have a situation where a ContextMenu is set for a TableView and the TableView is set as user data for the context menu, an event handler for the ActionEvent is registered for the MenuItems.
UPDATE 1: Minimal Reproducible Example
package org.example.tableview;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
public class TableViewExample extends Application {
#Override
public void start(Stage stage) throws Exception {
PersonTable personTable1 = new PersonTable(Arrays.asList(new Person("Max", "Muster"), new Person("John", "Doe")));
Tab tab1 = new Tab("Tab 1");
tab1.setContent(personTable1);
PersonTable personTable2 = new PersonTable(Arrays.asList(new Person("Petar", "Petrović"), new Person("Jane", "Q")));
Tab tab2 = new Tab("Tab 2");
tab2.setContent(personTable2);
TabPane tabPane = new TabPane(tab1, tab2);
stage.setScene(new Scene(tabPane));
stage.show();
}
static class Person {
final String givenName;
final String surname;
Person(String givenName, String surname) {
this.givenName = givenName;
this.surname = surname;
}
}
static class PersonTable extends TableView<Person> {
TableColumn<Person, String> columnGivenName = new TableColumn<>("Given name");
TableColumn<Person, String> surname = new TableColumn<>("Surname");
PersonTable(List<Person> persons) {
columnGivenName.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().givenName));
surname.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().surname));
getColumns().addAll(columnGivenName, surname);
getItems().addAll(persons);
setContextMenu(new ContextMenuPersonTable(this));
}
}
static class ContextMenuPersonTable extends ContextMenu {
MenuItem menuItemAlertSurname = new MenuItem("Alert Surname");
ContextMenuPersonTable(PersonTable table) {
setUserData(table);
menuItemAlertSurname.setAccelerator(KeyCombination.keyCombination("CTRL+SHIFT+S"));
menuItemAlertSurname.setOnAction(TableViewExample::handleAlertAction);
getItems().add(menuItemAlertSurname);
}
}
static void handleAlertAction(ActionEvent event) {
MenuItem menuItem = (MenuItem) event.getSource();
ContextMenu contextMenu = menuItem.getParentPopup();
PersonTable table = (PersonTable) contextMenu.getUserData();
TableView.TableViewSelectionModel<Person> selectionModel = table.getSelectionModel();
Person selectedItem = selectionModel.getSelectedItem();
if (selectedItem != null) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setContentText(">>> " + selectedItem.surname);
alert.showAndWait();
}
}
}
While working on the MRE I found out that the problem must be connected with the TabView and the number of Tabs: If there is only one tab the accelerator works perfectly fine. If there is more then one tab it only works fine on the second tab. The context menu works always.
UPDATE 2: I think the problem has to do with the fact that the same X is used for MenuItems in several ContextMenus. But to me that sounds like a common pattern. Is there a better solution?

Related

How to create table with vertical/column header in JavaFX

Is there a way to create tableview with vertical headings ? I don't see any option in javafx to do this.
You can set the graphic to a Label which is rotated, and set the text to an empty string.
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.setText(column.getText());
column.setText("");
label.setRotate(90);
column.setGraphic(label);
}
Here's a complete example:
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.setText(column.getText());
column.setText("");
label.setRotate(90);
column.setGraphic(label);
}
#Override
public void start(Stage stage) throws IOException {
TableView<Item> table = new TableView<>();
TableColumn<Item, Number> idColumn = new TableColumn<>("Id");
idColumn.setCellValueFactory(data -> new SimpleIntegerProperty(data.getValue().id()));
TableColumn<Item, String> itemColumn = new TableColumn<>("Item");
itemColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().name()));
makeColumnHeader(idColumn);
makeColumnHeader(itemColumn);
table.getColumns().add(idColumn);
table.getColumns().add(itemColumn);
for (int i = 1 ; i <= 20; i++) table.getItems().add(new Item(i, "Item "+i));
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static record Item(int id, String name){}
public static void main(String[] args) {
launch();
}
}
Note that setting the column's text to an empty string can have undesirable side effects. For example, the tableMenuButton relies on the text in the table columns to display the menu items. Add table.setTableMenuButtonVisible(true); to the code above to see the problem.
A slightly more robust solution is to bind the text of the label in the graphic to the text in the column, and then use CSS to hide the default text:
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.textProperty().bind(column.textProperty());
label.setRotate(90);
column.setGraphic(label);
}
and in an external style sheet:
.table-column > .label {
-fx-content-display: graphic-only;
}
I had to adapt the solution from #James_D to properly size the label by applying a minWidth and wrapping it in a Group: (Tested with openjfx19)
private void makeColumnHeader(TableColumn<?, ?> column, String text) {
Label label = new Label();
label.setText(text);
label.setRotate(-90);
label.setMinWidth(80);
column.setGraphic(new Group(label));
column.getStyleClass().add("rotated");
}

Javafx: On button click open second selection window

I'm making a button which opens a new window containing a gridview of members, which shows their name and rank. A member can be clicked to be set as currentMember. However my JavaFX knowledge is limited and I have no idea how to create this temporary window without changing the current controller. I'm not sure if what I have is a good way of doing it. Am I doing something wrong or is this the correct way of doing it?
This code gets run when the Select Person button is clicked
#FXML
private void setLid(ActionEvent event) {
Stage stage = new Stage();
VBox box = new VBox();
box.setPadding(new Insets(10));
TableView<Persoon> tablePers = new TableView<>();
TableColumn<Persoon, String> voornaam = new TableColumn<>();
TableColumn<Persoon, String> achternaam = new TableColumn<>();
TableColumn<Persoon, String> graad = new TableColumn<>();
voornaam.setCellValueFactory(cellData -> cellData.getValue().voornaamProperty());
voornaam.setCellFactory(TextFieldTableCell.forTableColumn());
achternaam.setCellValueFactory(cellData -> cellData.getValue().achternaamProperty());
graad.setCellValueFactory(cellData -> cellData.getValue().graadProperty());
Label label = new Label("Selecteer een persoon");
Button btnSelectCurrentLid = new Button();
btnSelectCurrentLid.setText("Bevestigen");
btnSelectCurrentLid.setOnAction((ActionEvent e) -> {
geselecteerdePersoon = (tablePers.getSelectionModel().getSelectedItem());
stage.close();
});
box.getChildren().add(label);
box.getChildren().add(tablePers);
box.getChildren().add(btnSelectCurrentLid);
Scene scene = new Scene(box, 250, 150);
stage.setScene(scene);
stage.show();
}
Here is a one-file mcve (copy paste the entire code into FxMain.java and run):
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FxMain extends Application {
private Persoon geselecteerdePersoon;
#Override
public void start(Stage primaryStage) throws Exception{
Label selectedPersonInfo = new Label();
Button start = new Button("Show Tabel");
start.setOnAction(e-> {
new LoginDialog().showAndWait();
selectedPersonInfo.setText("Selected: " +geselecteerdePersoon.voornaamProperty().get()
+ " " +geselecteerdePersoon.achternaamProperty().get());
});
BorderPane root = new BorderPane(selectedPersonInfo);
root.setBottom(start);
primaryStage.setScene(new Scene(root, 300,300));
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
class LoginDialog extends Dialog {
public LoginDialog() {
VBox box = new VBox();
box.setPadding(new Insets(10));
TableView<Persoon> tablePers = new TableView<>();
TableColumn<Persoon, String> voornaam = new TableColumn<>("Name");
TableColumn<Persoon, String> achternaam = new TableColumn<>("Last Name");
TableColumn<Persoon, String> graad = new TableColumn<>("Grade");
voornaam.setCellValueFactory(cellData -> cellData.getValue().voornaamProperty());
voornaam.setCellFactory(TextFieldTableCell.forTableColumn());
achternaam.setCellValueFactory(cellData -> cellData.getValue().achternaamProperty());
graad.setCellValueFactory(cellData -> cellData.getValue().graadProperty());
tablePers.getColumns().addAll(voornaam, achternaam, graad);
tablePers.getItems().add(new Persoon("Alice", "Bee","70"));
tablePers.getItems().add(new Persoon("Charly", "Din","32"));
Label label = new Label("Selecteer een persoon");
Button btnSelectCurrentLid = new Button();
btnSelectCurrentLid.setText("Bevestigen");
btnSelectCurrentLid.setOnAction((ActionEvent e) -> {
geselecteerdePersoon = tablePers.getSelectionModel().getSelectedItem();
close();
});
box.getChildren().addAll(label, tablePers, btnSelectCurrentLid);
getDialogPane().setContent(box);
getDialogPane().getButtonTypes().addAll(ButtonType.CANCEL);
}
}
}
class Persoon {
private final SimpleStringProperty lName;
private final SimpleStringProperty fName;
private final SimpleStringProperty grade;
public Persoon(String fName, String lName, String grade) {
this.fName = new SimpleStringProperty(fName);
this.lName = new SimpleStringProperty(lName);
this.grade = new SimpleStringProperty(grade);
}
public final StringProperty achternaamProperty() { return lName; }
public final StringProperty voornaamProperty() { return fName; }
public final StringProperty graadProperty() { return grade; }
}

JavaFx property binding with multiple objects on on screen

I use JavaFx with property binding.
I got a object 'Person' with the properties 'name' and age.
These objects are stored in a ObservableList.
The properties are bound to labels on the gui. When I change the person in the ListBox the data also change on the right hand side.
GUI with person list:
And now it comes to my problem.
I want to disply all persons on one window, like the next picture shows.
GUI with multiple persons on one view:
How can I handle this. I thought about HBox but the binding doesn't work.
FYI: Here you can find the tutorial I used.
https://code.makery.ch/library/javafx-tutorial/part1/
This looks like a perfect time to use a ListView with custom ListCell implementations.
The sample application below shows a very basic application that displays each Person object in a ListView. We will provide our own ListCell so we can control exactly how each Person gets displayed.
I also added a profile photo just for fun :)
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewDetailSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// First, let's create our list of Persons
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("John", 34),
new Person("Cheyenne", 24),
new Person("Micah", 17),
new Person("Katelyn", 28)
);
// Create a ListView
ListView<Person> listView = new ListView<>();
// Bind our list to the ListView
listView.setItems(persons);
// Now, for the magic. We'll create our own ListCells for the ListView. This allows us to create a custom
// layout for each individual cell. For this sample, we'll include a profile picture, the name, and the age.
listView.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
#Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<Person>() {
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
// Set any empty cells to show nothing
if (person == null || empty) {
setText(null);
setGraphic(null);
} else {
// Here we can build our layout. We'll use a HBox for our root container
HBox cellRoot = new HBox(5);
cellRoot.setAlignment(Pos.CENTER_LEFT);
cellRoot.setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/sample/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
cellRoot.getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
cellRoot.getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the cellRoot
cellRoot.getChildren().add(vBox);
// Finally, set this cell to display our custom layout
setGraphic(cellRoot);
}
}
};
}
});
// Now, add our ListView to the root layout
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(450);
primaryStage.setHeight(400);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
// Simple Person class
class Person {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
public Person(String name, int age) {
this.name.set(name);
this.age.set(age);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
}
The Result:
Without ListView:
If you'd prefer not to use a ListView for this display, you can keep another list of your Person displays and bind that to the children list of whichever container you want:
// Create a list to hold our individual Person displays
ObservableList<Node> personDisplays = FXCollections.observableArrayList();
// Now add a new PersonDisplay to the list for each Person in the personsList
persons.forEach(person -> personDisplays.add(new PersonDisplay(person)));
// Bind our personsDisplay list to the children of our root VBox
Bindings.bindContent(root.getChildren(), personDisplays);
PersonDisplay class:
class PersonDisplay extends HBox {
public PersonDisplay(Person person) {
// First, let's configure our root layout
setSpacing(5);
setAlignment(Pos.CENTER_LEFT);
setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the layout
getChildren().add(vBox);
}
}
The Result:

Data not getting displayed in Java FX Table when pressing button

I am working with Table in Java FX. The code is below :
package addsubject;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class AddSubject extends Application {
private TableView<Subject> table = new TableView<Subject>();
private final ObservableList<Subject> data
= FXCollections.observableArrayList(new Subject("Mobile Computing", "5623"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Add Subject");
stage.setWidth(700);
stage.setHeight(600);
final Label label = new Label("Subject Details");
label.setFont(new Font("Calibri", 20));
table.setEditable(true);
TableColumn sub = new TableColumn("Subject Name");
sub.setMinWidth(400);
sub.setCellValueFactory(
new PropertyValueFactory<Subject, String>("sub"));
sub.setCellFactory(TextFieldTableCell.forTableColumn());
sub.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Subject, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Subject, String> t) {
((Subject) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setSubName(t.getNewValue());
}
}
);
TableColumn code = new TableColumn("Subject Code");
code.setMinWidth(150);
code.setCellValueFactory(
new PropertyValueFactory<Subject, String>("code"));
code.setCellFactory(TextFieldTableCell.forTableColumn());
code.setCellFactory(TextFieldTableCell.forTableColumn());
code.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Subject, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Subject, String> t) {
((Subject) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setSubCode(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(sub, code);
final TextField addSubName = new TextField();
addSubName.setPromptText("Subject Name");
addSubName.setPrefWidth(350);
final TextField addCode = new TextField();
addCode.setPrefWidth(150);
addCode.setPromptText("Subject Code");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Subject(
addSubName.getText(),
addCode.getText()));
addSubName.clear();
addCode.clear();
}
});
hb.getChildren().addAll(addSubName, addCode, addButton);
hb.setSpacing(5);
final VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Subject {
private final SimpleStringProperty sub;
private final SimpleStringProperty code;
private Subject(String subName, String subCode) {
this.sub = new SimpleStringProperty(subName);
this.code = new SimpleStringProperty(subCode);
}
public String getSubName() {
return sub.get();
}
public void setSubName(String subName) {
sub.set(subName);
}
public String getSubCode() {
return code.get();
}
public void setSubCode(String subCode) {
code.set(subCode);
}
}
}
I will say its working.
When I run the code, the Table should contain one row of information which is stored in 'data'. It is not displayed in the table when I run the code. How can I make it displayed ?
Next thing is to add new data to the Table when the 'Add' button is pressed after filling the 2 TextFields. I filled the 2 Text Fields, pressed the button and nothing appeared in the Table. How can I make it appear ?
The PropertyValueFactories are wrong. For example
new PropertyValueFactory<Subject, String>("sub")
This means your data object should have a method getSub() to return the value. But yours is
public String getSubName() {
return sub.get();
}
Either rename the Getter to getSub() or use new PropertyValueFactory<Subject, String>("subName")
The same applies to subject code.
You should read about JavaBeans naming conventions. Or this article on JavaFX Properties and Binding
Try implementing the following functions:
public StringProperty subProperty(){
return sub;
}
public StringProperty codeProperty(){
return code;
}
Why are these members final?

How to dynamically add data to the tableView only in the last rows without disturbing other rows with observavbleList in javafx?

In nutshell I want to add the row silently at the end of the table without affecting other rows.
This is my controller class code excerpt to add entries to the table. The problem is when I add an item to the sourceTree, instead of adding a row at the end silently it disturbs the complete table. Suppose there were already some entries in the table and the user expands one of the titled pane in the table, now when a new row will be added to the table, complete table will blink and this titled pane will automatically shrink. This blinking led me to conclude that instead of adding an entry whole table is refreshing probably. Please help...
#FXML
public static TableView<NodeInfo> tableView;
#FXML
public static TableColumn<NodeInfo, TitledPane> nodeTree;
#FXML
private TableColumn<NodeInfo, String> name;
#FXML
private TableColumn<NodeInfo, CheckBox> favourite;
#FXML
private TableColumn<NodeInfo, Button> updates;
#FXML
public static ObservableList<NodeInfo> sourceTree = FXCollections.observableArrayList();
tableView.setPlaceholder(new Label("You have no Nodes in the network at the moment!!!"));
nodeTree.setCellValueFactory(new PropertyValueFactory<NodeInfo, TitledPane>("TitledPaneNode"));
nodeTree.prefWidthProperty().bind(tableView.widthProperty().divide(3));
name.setCellValueFactory(new PropertyValueFactory<NodeInfo, String>("name"));
name.prefWidthProperty().bind(tableView.widthProperty().divide(3));
favourite.setCellValueFactory(new PropertyValueFactory<NodeInfo, CheckBox>("favourite"));
favourite.prefWidthProperty().bind(tableView.widthProperty().divide(6));
updates.setCellValueFactory(new PropertyValueFactory<NodeInfo, Button>("NoOfUpdates"));
updates.prefWidthProperty().bind(tableView.widthProperty().divide(6));
tableView.setItems(sourceTree);
The following is a SSCCE code I have simulated your use case. At the result there was no table disturbing, or pane shrinking. Test it yourself and compare it with yours:
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.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TitledPane;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class PaneDemo extends Application {
private TableView<NodeInfo> table = new TableView<NodeInfo>();
private final ObservableList<NodeInfo> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setWidth(450);
stage.setHeight(500);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<NodeInfo, String>("firstName"));
TableColumn paneCol = new TableColumn("Pane");
paneCol.setMinWidth(100);
paneCol.setCellValueFactory(new PropertyValueFactory<NodeInfo, TitledPane>("titledPane"));
for (int i = 0; i < 5; i++) {
TitledPane pane = new TitledPane("title " + i, new Text("text " + i));
data.add(new NodeInfo("name " + i, pane));
}
table.setItems(data);
table.getColumns().addAll(firstNameCol, paneCol);
Button btn = new Button("add new item");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
TitledPane pane = new TitledPane("title new", new Text("text new"));
data.add(new NodeInfo("name new", pane));
}
});
final VBox vbox = new VBox(20);
vbox.getChildren().addAll(table, btn);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class NodeInfo {
private final SimpleStringProperty firstName;
private TitledPane titledPane;
private NodeInfo(String fName, TitledPane titledPane) {
this.firstName = new SimpleStringProperty(fName);
this.titledPane = titledPane;
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public TitledPane getTitledPane() {
return titledPane;
}
public void setTitledPane(TitledPane fName) {
titledPane = fName;
}
}
}

Resources