Bind TableView to ObservableList - javafx

I have an observable List saving Author objects. The gui is able to add an author to my database. The observable list contains all the objects of the database. I want my table to update automatically if I add an Author in the databse.
I have already tried to refresh the list with table.refresh(). I am also thinking about using a change listener for the observable list.
Here the Code for creating the table. authorList is an observable list. I think I don't quite understand how to use an observable list. My suggestion was that by using "table.setItem(authorList)", my table automatically updates its entries if something is changed in the list. Obviously this is not the case.
private void createAuthorsTablePane() {
// TODO: Layout ändern
GridPane authorGridPane = new GridPane();
// create table
TableView<Author> table = new TableView<>();
// Create columns with title
TableColumn<Author, String> idColumn = new TableColumn<>("ID");
TableColumn<Author, String> nameColumn = new TableColumn<>("Name");
TableColumn<Author, String> emailColumn = new TableColumn<>("Email");
TableColumn<Author, String> publicationsColumn = new TableColumn<>("Publications");
// Add columns to table node
table.getColumns().add(idColumn);
table.getColumns().add(nameColumn);
table.getColumns().add(emailColumn);
table.getColumns().add(publicationsColumn);
// Bindings
PropertyValueFactory<Author, String> idColumnFactory = new PropertyValueFactory<>("id");
PropertyValueFactory<Author, String> nameColumnFactory = new PropertyValueFactory<>("name");
PropertyValueFactory<Author, String> emailColumnFactory = new PropertyValueFactory<>("email");
PropertyValueFactory<Author, String> publicationsColumnFactory = new PropertyValueFactory<>("publications");
idColumn.setCellValueFactory(idColumnFactory);
nameColumn.setCellValueFactory(nameColumnFactory);
emailColumn.setCellValueFactory(emailColumnFactory);
publicationsColumn.setCellValueFactory(publicationsColumnFactory);
table.setItems(authorList);
// Create Buttons
createAuthorButton = new Button("Create author");
createAuthorButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
mainController.createAuthorController();
}
});
deleteAuthorButton = new Button("Delete selected author");
// Add Nodes to Pane
authorGridPane.add(new Label("Authors"), 0, 0);
authorGridPane.add(table, 0, 1);
authorGridPane.add(deleteAuthorButton, 0, 2);
authorGridPane.add(createAuthorButton, 1, 2);
authorPane = authorGridPane;
}
Here is the class, where I create my authorList. I am registering the list in the class where I create the table by using a controller.
public class ObservableModel {
private ObservableList<Publication> publicationList;
private ObservableList<Author> authorList;
public ObservableModel(DatabaseService database) {
publicationList = FXCollections.observableList(database.getPublications());
authorList = FXCollections.observableList(database.getAuthors());
}
public ObservableList<Publication> getPublicationList() {
return publicationList;
}
public ObservableList<Author> getAuthorList() {
return authorList;
}
}

TableView<Author> table = new TableView<>();
private ObservableList<Author> authorList = FXCollections.observableArrayList();
private Property<ObservableList<Author>> authorListProperty = new SimpleObjectProperty<>(authorList);
table.itemsProperty().bind(authorListProperty); // The Binding
Every time you change authorList the tableview will be updated as well

idcolumnFactory.setCellValueFactory(cellData -> cellData.getValue().getIdProperty());
or if you declare simple bean
idcolumnFactory.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getId()));
don't forget
table.setItems(your_observale_list);

Related

Using filteredList in an editable ListView

I'm using a FilteredList for my ListView to enable searching. The problem is that FilteredList does not allow mutating the data in any way, it only responds to changes in underlying ObservableList.
Also, it is declared final, so I can't simply extend it to forward the edit requests to the source.
So, how can I use it in an Editable ListView?
Here is the code to reproduce the problem
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
//problematic code
var observableList = FXCollections.observableArrayList("name", "name 2", "name 3");
FilteredList<String> filteredList = new FilteredList<>(observableList);
var list = new ListView<>(filteredList);
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());
//boilerplate code
VBox wrapper = new VBox(list);
primaryStage.setScene(new Scene(wrapper));
primaryStage.show();
}
}
Edit: added an minimal reproducible example
The problem - as you noticed - is that none of the concrete implementations of TransformationList (Sorted/FilteredList) is modifiable. So the default commit handler fails (with UnsupportedOperationException) while trying to set the newValue:
private EventHandler<ListView.EditEvent<T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
int index = t.getIndex();
List<T> list = getItems();
if (index < 0 || index >= list.size()) return;
list.set(index, t.getNewValue());
};
The way out is a custom commit handler. Its implementation depends on context, it can
set a new item in the underlying source list
modify a property of the item
Code snippet for setting an item:
// monolithic items
ObservableList<String> data = FXCollections.observableArrayList("afirst", "abString", "other");
FilteredList<String> filteredData = new FilteredList<>(data);
filteredData.setPredicate(text -> text.contains("a"));
// set up an editable listView
ListView<String> list = new ListView<>(filteredData);
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());
// commitHandler resetting the underlying data element
list.setOnEditCommit(v -> {
ObservableList<String> items = list.getItems();
int index = v.getIndex();
if (items instanceof TransformationList<?, ?>) {
TransformationList transformed = (TransformationList) items;
items = transformed.getSource();
index = transformed.getSourceIndex(index);
}
items.set(index, v.getNewValue());
});
Code snippet for changing a property of an item:
// items with properties
ObservableList<MenuItem> data = FXCollections.observableArrayList(
new MenuItem("afirst"), new MenuItem("abString"), new MenuItem("other"));
FilteredList<MenuItem> filteredData = new FilteredList<>(data);
// filter on text property
filteredData.setPredicate(menuItem -> menuItem.getText().contains("a"));
// set up an editable listView
ListView<MenuItem> list = new ListView<>(filteredData);
list.setEditable(true);
// converter for use in TextFieldListCell
StringConverter<MenuItem> converter = new StringConverter<>() {
#Override
public String toString(MenuItem menuItem) {
return menuItem != null ? menuItem.getText() : null;
}
#Override
public MenuItem fromString(String text) {
return new MenuItem(text);
}
};
list.setCellFactory(TextFieldListCell.forListView(converter));
// commitHandler changing a property of the item
list.setOnEditCommit(v -> {
ObservableList<MenuItem> items = list.getItems();
MenuItem column = items.get(v.getIndex());
MenuItem standIn = v.getNewValue();
column.setText(standIn.getText());
});

Setting a style class only for first-level nodes in JavaFX treeTableView

I have a restaurant menu with dishes and categories implemented as a treeTableView in javaFX.
I want to make the the category rows appear different with CSS but I just can't find a way to filter them out and apply a class. Moving the images a bit to the left would also be nice. I also had no luck using a rowFactory. I've seen this answer but I don't understand it.
This is how I fill the table. I've left out the column- and cellfactories.
private void fillDishes(List<Dish> dishes){
root.getChildren().clear();
Map<String,TreeItem<Dish>> categoryMap = new HashMap<>();
for (Category c: allCats) {
TreeItem<Dish> newCat = new TreeItem<>(new Dish(c.getName(),null,null,null));
//newCat.getGraphic().getStyleClass().add("category");
categoryMap.put(c.getName(),newCat);
root.getChildren().add(newCat);
}
for (Dish d: dishes) {
categoryMap.get(d.getCategory()).getChildren().add(new TreeItem<>(d));
}
}
TreeTableView uses the rowFactory to create the TreeTableRows. At some time later it assigns a TreeItem to a TreeTableRow. This may happen again with different TreeItems for the same row. For this reason you need to handle changes those changes which can be done by adding a ChangeHandler to the TreeTableRow.treeItem property. If a new TreeItem is assigned to the row, you can check for top-level nodes by checking the children of the (invisible) root item for the row item.
I prefer the approach that does not require searching the child list though. It's possible to compare the parent of the item with the root.
public static class Item {
private final String value1;
private final String value2;
public Item(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
}
#Override
public void start(Stage primaryStage) {
final TreeItem<Item> root = new TreeItem<>(null);
TreeTableView<Item> ttv = new TreeTableView<>(root);
ttv.setShowRoot(false);
TreeTableColumn<Item, String> column1 = new TreeTableColumn<>();
column1.setCellValueFactory(new TreeItemPropertyValueFactory<>("value1"));
TreeTableColumn<Item, String> column2 = new TreeTableColumn<>();
column2.setCellValueFactory(new TreeItemPropertyValueFactory<>("value2"));
ttv.getColumns().addAll(column1, column2);
final PseudoClass topNode = PseudoClass.getPseudoClass("top-node");
ttv.setRowFactory(t -> {
final TreeTableRow<Item> row = new TreeTableRow<>();
// every time the TreeItem changes, check, if the new item is a
// child of the root and set the pseudoclass accordingly
row.treeItemProperty().addListener((o, oldValue, newValue) -> {
boolean tn = false;
if (newValue != null) {
tn = newValue.getParent() == root;
}
row.pseudoClassStateChanged(topNode, tn);
});
return row;
});
// fill tree structure
TreeItem<Item> c1 = new TreeItem<>(new Item("category 1", null));
c1.getChildren().addAll(
new TreeItem<>(new Item("sub1.1", "foo")),
new TreeItem<>(new Item("sub1.2", "bar")));
TreeItem<Item> c2 = new TreeItem<>(new Item("category 2", null));
c2.getChildren().addAll(
new TreeItem<>(new Item("sub2.1", "answer")),
new TreeItem<>(new Item("sub2.2", "42")));
root.getChildren().addAll(c1, c2);
Scene scene = new Scene(ttv);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
style.css
.tree-table-row-cell:top-node {
-fx-background: orange;
}
Moving the images a bit to the left would also be nice.
Usually you do this from a custom TreeTableCell returned by a TreeTableColumn.cellFactory. Depending on the behavior you want to implement setting fitWidth/fitHeight may be sufficient, but in other cases dynamically modifying those values based on the cell size may be required.

Javafx TableView not showing data

I used ObservableList to populate the TableView but the problem is that the data is not showing in the table I don't know what is the problem because the number of rows is exactly like I added them capture but there is nothing in the cells!
here is the code of the controller:
public class EnlistDim {
private static final String DEFAULT="-fx-text-background-color: black; -fx-background-color: steelblue;-fx-fill: red ;";
#FXML
private TableView<Parameter> tab;
#FXML
public void initialize() {
final ObservableList<Parameter> data = FXCollections.observableArrayList(
new Parameter("Query","Access method","Sequential scan"),
new Parameter("Query","Access method","in memory"),
new Parameter("Query","Operation","join"),
new Parameter("Query","Operation","Scan"),
new Parameter("Query","Operation","Sort"),
new Parameter("Database","Buffer management","Without buffer"),
new Parameter("Database","Buffer management","FIFO"),
new Parameter("Database","Buffer management","LIFO"),
new Parameter("Database","Buffer management","LRU"),
new Parameter("Database","Buffer management","Other"),
new Parameter("Database","Optimization structure","Not used"),
new Parameter("Database","Optimization structure","Partionning"),
new Parameter("Database","Optimization structure","Materialized View"),
new Parameter("Database","Optimization structure","compresssion"),
new Parameter("Database","System storage type","Database SQL"),
new Parameter("Database","System storage type","New SQL"),
new Parameter("Database","System storage type","Document"),
new Parameter("Database","System storage type","Graph"),
new Parameter("Database","System storage type","NVRAM"),
new Parameter("Database","System storage type","key value store"),
new Parameter("Database","Data storage type","Row Oriented"),
new Parameter("Database","Data storage type","Column Oriented"),
new Parameter("Database","Data storage type","Hybrid Oriented"),
new Parameter("Hardware","Processing device","CPU"),
new Parameter("Hardware","Processing device","GPU"),
new Parameter("Hardware","Processing device","FPGA"),
new Parameter("Hardware","Storage device","RAM"),
new Parameter("Hardware","Storage device","SSD"),
new Parameter("Hardware","Storage device","NVRAM"),
new Parameter("Hardware","Communication device","Modem"),
new Parameter("Hardware","Communication device","Cable"),
new Parameter("Hardware","Communication device","FaxModem"),
new Parameter("Hardware","Communication device","Router")
);
tab.setEditable(true);
tab.setItems(data);
tab.setStyle(DEFAULT);
}
}
and the code of Parameter class:
class Parameter {
SimpleStringProperty cat;
SimpleStringProperty subCat;
SimpleStringProperty subSubCat;
Parameter(String cat, String subCat, String subSubCat) {
this.cat = new SimpleStringProperty(cat);
this.subCat = new SimpleStringProperty(subCat);
this.subSubCat = new SimpleStringProperty(subSubCat);
}
public String getCat() {
return cat.get();
}
public void setCat(String c) {
cat.set(c);
}
public String getSubCat() {
return subCat.get();
}
public void setSubCat(String sc) {
subCat.set(sc);
}
public String getSubSubCat() {
return subSubCat.get();
}
public void setSubSubCat(String ssc) {
subSubCat.set(ssc);
}
}
You need to actually tell the TableView HOW to display the data. This is done using a CellValueFactory. Basically, you need to tell each column of the table what type of data it holds and where it gets that data from.
You need to start by defining your columns (give them an fx:id either in the FXML file or in SceneBuilder):
#FXML
TableColumn<Parameter, String> colCategory;
#FXML
TableColumn<Parameter, String> colSubCategory;
#FXML
TableColumn<Parameter, String> colSubSubCategory;
Each TableColumn takes two Type parameters. The first defines the object being displayed (Parameter). The second is the data type for this column (all yours are String).
Once the columns are defined, you need to set their CellValueFactory in your initialize() method:
colCategory.setCellValueFactory(new PropertyValueFactory<Parameter, String>("cat"));
colSubCategory.setCellValueFactory(new PropertyValueFactory<Parameter, String>("subCat"));
colSubSubCategory.setCellValueFactory(new PropertyValueFactory<Parameter, String>("subSubCat"));
Here you are telling each column where to find the data to be displayed. The last argument on the line, in the quotes, is the name of your property within the Parameter object.
So, when JavaFX populates your table, it will takes these steps to populate each column (colCategory, for example):
Get the CellValueFactory for colCategory.
The factory is a PropertyValueFactory, so determine which class holds the property (in this case it is the Parameter class)
Look in the Parameter class for a String property by the name of "cat"
Populate the column's cell with the value of the cat property.

JavaFX retrieve TableCells of selected row

In my JavaFX TableView, I am trying to retrieve TableCells from a selected row to mark them
with custom colors.
Simply changing the colors of the entire row does not work in this case, as I use different color shadings in each cell depending
on the value of each cell
The example below shows two approaches I tried I to solve the problem
1) Use a listener to retrieve cells in the selected row. Printing the row index and content already works
However, I could not find how to retrieve a TableCell from table.getSelectionModel().
2) Try a dirty workaround to add the TableCells to a global data structure in the columnCellFactory.
However, the TableCells do not get added to the tableCells ArrayList for some reason.
To obtain a short example, the imports and the Classes defining the EditingCell (custom TableCell) and CellEditEvent were omitted.
package TableViewColExample;
public class TableViewExample extends Application {
private Callback<TableColumn, TableCell> columnCellFactory ;
final TableView<String[]> table = new TableView<String[]>();
ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();
ObservableSet<String> selectedRows = FXCollections.observableSet();
ArrayList<ArrayList<EditingCell>> tableColumns = new ArrayList<ArrayList<EditingCell>>();
#Override
public void start(Stage stage) {
String[][] dat = new String[][]{
{"C1","C2","C3"},{"a","b","c"},{"d","e","f"},{"g","i","h"}};
ObservableList<String []> data = FXCollections.observableArrayList();
data.addAll(Arrays.asList(dat));
data.remove(0);
table.setItems(data);
for (int i = 0; i < dat[0].length; i++) {
TableColumn tc = new TableColumn(dat[0][i]);
final int colNo = i;
tc.setCellValueFactory(new Callback<CellDataFeatures<String[], String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<String[], String> p) {
return new SimpleStringProperty((p.getValue()[colNo]));
}
});
ArrayList<EditingCell> tableCells = new ArrayList<EditingCell>();
columnCellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
EditingCell tcell = new EditingCell();
//For some reason, the EditingCell is never added to the list
tableCells.add(tcell);
return tcell;
}
};
tc.setCellFactory(columnCellFactory);
tableColumns.add(tableCells);
//The printed value here is 0, which means that the Factory does not add the Editing Cell to the List
System.out.println(" Column rows "+tableCells.size());
table.getColumns().add(tc);
}
//Output: TableColumns 3, TableRows 0
System.out.println("TableColumns "+ tableColumns.size() + " Table rows "+tableColumns.get(0).size());
table.setItems(data);
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {
selectedRows.clear();
table.getSelectionModel().getSelectedCells().stream().map(TablePosition::getRow).f orEach(row -> {
selectedRowIndexes.add(row);
System.out.println(selectedRowIndexes.toString());
});
table.getSelectionModel().getSelectedItems().forEach(row -> {
selectedRows.add(Arrays.toString(row));
System.out.println(selectedRows.toString());
});
});
stage.setScene(new Scene(table));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Bind column to data javafx

Let me first explain what I did.
I am having list of tables in the drop down
when someone select any one it will populate all the data to tableview with respect to column name
I am able to get the columns from the table and put it in the tableview but I am not able to bind the data with associated columns.
My code here is to look
this.tableName = clsComboData.getValue();
System.out.println(tableName); System.out.println("Button Pressed");
List<StaticColumnConfig> allColumns = null;
for(StaticDataTable dataTable : dropdown) {
if(dataTable.getTableName() != null && dataTable.getTableName().equalsIgnoreCase(tableName)) {
System.out.println(dataTable.getColumnConfig());
allColumns = dataTable.getColumnConfig();
}
}
switch (tableName) {
case "IOSwapCounterparties": SimpleDateFormat date = new SimpleDateFormat("2015-04-15");
System.out.println("Into the Switch");
data = FXCollections.observableArrayList(
new IOSwapCounterparties(1,"a","b","c","d","e",date),
new IOSwapCounterparties(1,"aa","bb","cv","dd","es",date),
new IOSwapCounterparties(1,"ad","bd","cd","dc","eb",date),
new IOSwapCounterparties(1,"aw","bw","cr","dt","ey",date),
new IOSwapCounterparties(1,"ag","bt","cy","du","ep",date)
);
break;
}
//System.out.println("Its in ELSE");
//"Invited" column
TableColumn checkboxCol = new TableColumn<Person, Boolean>();
checkboxCol.setText("");
checkboxCol.setMinWidth(50);
checkboxCol.setCellValueFactory(new PropertyValueFactory("checkbox"));
// Create checkboxes
checkboxCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> p) {
CheckBoxTableCell<Person, Boolean> checkBox = new CheckBoxTableCell();
return checkBox;
}
});
//Set cell factory for cells that allow editing
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
for(StaticColumnConfig column : allColumns){
TableColumn oneColumn = new TableColumn(column.getColumnName());
oneColumn.setCellFactory(cellFactory);
}
// Clear the tableview for next table
tblViewer.getColumns().clear();
// Push the data to the tableview
tblViewer.setItems(data);
tblViewer.setEditable(true);
tblViewer.getColumns().addAll(checkboxCol);
for(StaticColumnConfig column : allColumns){
System.out.println(column.getColumnName());
TableColumn oneColumn = new TableColumn(column.getColumnName());
tblViewer.getColumns().addAll(oneColumn);
}
// Add the columns
tblViewer.getSelectionModel().setCellSelectionEnabled(true);
tblViewer.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
There is been uppercase - lowercase mistake with my getter and setter method.

Resources