JavaFX 8 update TableView after new Data added - javafx

My TableView is not updating, do i need a listener ?
(m is my Model)
#FXML
private TableView<Mitarbeiter> mitarbeiter;
ObservableList<Mitarbeiter> data =
FXCollections.observableArrayList(m.getMitarbeiterListe()
);
mitarbeiter.setItems(data);
public ArrayList getMitarbeiterListe(){
return mitarbeiterliste;
}
In a new Stage i add some Mitarbeiter to the List in my Model
m.addMitarbeiterToList(mitarbeiter)
public void addMitarbeiterToList(Mitarbeiter mitarbeiter){
mitarbeiterliste.add(mitarbeiter);
}
But the TableView in the other Stage is not updating the new data.
In the end, is the ObservableList not pointed to the ArrayList from the Model ?

Instead of adding items to mitarbeiterliste (which is an ArrayList, and is not observable), add them to data, which is the ObservableList holding the items for the table. The TableView observes this list and automatically updates the view when the list contents changes.
The context of your code snippets is not very clear, but you would do something like
public ArrayList getMitarbeiterListe(){
return data;
}
or instead of
mitarbeiterliste.add(mitarbeiter);
do
data.add(mitarbeiter);

Related

How to Generate Message According to Values in Multiple Fields?

I am building an application using JavaFX. What I am trying to do is generate a message according to the user input values. So there are one text-field and one combo-box and one check-box per row and there are many rows like the following.
Let's say I will generate three different messages according to the user values. So I need to check whether those fields are empty or not and check each field's value to generate a specific message. Checking fields are okay for just three rows like the above. But I have 10 fields. So I have to check each and generate or append my own message. And also if the user checked the check-box need to group all checked row values. So what I am asking is there any good way (best practice) to achieve what I need or an easy one also? I have tried with HashMap and ArrayList. But those are not working for this.
Really appreciate it if anybody can help me. Thanks in advance.
I would probably recommend a custom node that you create on your own like below. This example is not supposed to have the same functionality as your application but just to show how to create and use custom nodes. I kept your idea in mind when creating this example it has your textfield combobox and checkbox and they are a little easier to manage. Give it a run and let me know if you have any questions
public class Main extends Application {
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
ArrayList<String> itemList = new ArrayList<>(Arrays.asList("Dog", "Cat", "Turkey"));
ArrayList<HBoxRow> hBoxRowArrayList = new ArrayList<>();
for (int i = 0; i<3; i++) {
HBoxRow hBoxRow = new HBoxRow();
hBoxRow.setComboBoxValues(FXCollections.observableList(itemList));
hBoxRowArrayList.add(hBoxRow);
vBox.getChildren().add(hBoxRow.gethBox());
}
Button printTextfieldsButton = new Button("Print Textfields");
printTextfieldsButton.setOnAction(event -> {
for (HBoxRow hBoxRow : hBoxRowArrayList) {
System.out.println("hBoxRow.getTextFieldInput() = " + hBoxRow.getTextFieldInput());
}
});
vBox.getChildren().add(printTextfieldsButton);
stage.setScene(new Scene(vBox));
stage.show();
}
//Below is the custom Node
public class HBoxRow {
HBox hBox = new HBox();
ComboBox<String> comboBox = new ComboBox<>();
TextField textField = new TextField();
CheckBox checkBox = new CheckBox();
public HBoxRow(){
hBox.setAlignment(Pos.CENTER);
textField.setPrefWidth(150);
comboBox.setPrefWidth(150);
checkBox.setOnAction(event -> {
textField.setDisable(!textField.isDisabled());
comboBox.setDisable(!comboBox.isDisabled());
});
hBox.getChildren().addAll(checkBox, textField, comboBox);
}
public void setComboBoxValues(ObservableList observableList) {
comboBox.setItems(observableList);
}
public HBox gethBox(){
return hBox;
}
public String getTextFieldInput(){
return textField.getText();
}
}
}

Accessing the Value of a CheckBox in TableView

I'm having trouble getting the boolean value of whether a checkbox in a tableView for JavaFX is selected or not.
(Link to Image)
https://i.imgur.com/pIWDcfI.png
For some reason the when I getCellObservableValue() to get the CheckBox at index 1, I get null as the result.
//From SceneBuilder/JavaFX file
<TableColumn fx:id="labelColumn" prefWidth="112.57145690917969" text="Use
as Label" />
//Setting Up Table, which displays everything correctly
TableColumn<Integer,CheckBox> labelColumn = (TableColumn<Integer,
CheckBox>) elements.get("labelColumn");
labelColumn.setCellFactory(data -> new CheckBoxTableCell<>());
monitorTable.setEditable(true);
//Trying to Access, which gives null pointer exception
CheckBox cb = (CheckBox) labelColumn.getCellObservableValue(1);
System.out.println(cb.isSelected());
That method returns null if there's no cellValueFactory set. Besides, you should really have a model to hold this state—a TableView is just a view. Unlike the TableViewSelectionModel, which represents which items are selected solely in the context of the TableView itself, a column containing CheckBoxes represents the "boolean state" of a property of the model. For example:
public class ToDoTask {
private final StringProperty name = new SimpleStringProperty(this, "name");
private final BooleanProperty complete = new SimpleBooleanProperty(this, "complete");
// constructors, getters, setters, and property-getters omitted for brevity
}
A TableView for displaying that class could be configured like the following:
TableView<ToDoTask> table = new TableView<>();
table.setItems(...);
TableColumn<ToDoTask, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);
TableColumn<ToDoTask, Boolean> completeCol = new TableColumn<>("Complete");
completeCol.setCellValueFactory(features -> features.getValue().completeProperty());
completeCol.setCellFactory(CheckBoxTableCell.forTableColumn(completeCol));
table.getColumns().add(completeCol);
You would then query if a task is complete by accessing the model:
table.getItems().get(...).isComplete();
Another option to setting the cellValueFactory is to register a Callback with the CheckBoxTableCells themselves. See CheckBoxTableCell#forTableColumn(Callback).
Also, note that getCellObservableValue() returns an ObservableValue. A CheckBox is not an ObservableValue. If you weren't receiving null you'd be getting a ClassCastException.

JavaFX. Binding list of non-observables values with TableView [duplicate]

This question already has an answer here:
JavaBean wrapping with JavaFX Properties
(1 answer)
Closed 6 years ago.
I write my application that uses Tableview in which I want to represent and edit list of data.
I have data model. Something like
public class CModel
{
private List<CItem> m_lstItems;
public List<CItem> getList()
{
return m_lstItems;
}
}
public class CItem
{
private String m_sName;
private String m_sType;
public void setName(String s)
{
m_sName = s;
}
public String getName()
{
return new String(m_sName);
}
}
If I need to bind my data model I can create observableList(). But this doesn’t allow me to observe items editing. To make editing possible I need to inherit CItem members from Observable. If I declare it as Property TableView observes items changes.
The problem is that if CModel is pure data model I shouldn’t inherit it from Observable (because data and its view should be separated).
How can I wrap every list item with Observable or what is best approach?
For each column, create a CellValueFactory that wraps your POJO properties in a JavaFX Property. The example below does that on a fictive Person object that has a String property called name:
TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getName()));

How can I listen to INTERNAL changes in a tableview?

I have a TableView whose items contain checkboxes. As soon as 2 checkboxes are selected, I need to "unhide" a button.
I have no idea how to check that. Do you have an approach?
The items don't know each other.
The TableView-Controller holds the TableView and the TableColumns.
As far as I know you cannot use bindings here, since you cannot bind yourself to multiple properties. I'm glad for every kind of help. :)
EDIT: To clarify myself: tableView.getItems().addListener() won't work since this can only listen to modifications to the list and not to the outer elements. It can notice if "add()" or "remove" was called, but that's basically it as far as I know.
PS: Busy waiting in a seperate thread is no solution of course.
Assuming you have a TableView<Item> for some Item class with a BooleanProperty:
public class Item {
private final BooleanProperty checked = new SimpleBooleanProperty();
public BooleanProperty checkedProperty() {
return checked ;
}
public final boolean isChecked() {
return checkedProperty().get();
}
public final void setChecked(boolean checked) {
checkedProperty().set(checked);
}
// other properties, etc...
}
and your checkboxes are bound to this property, then you can create your items list using an extractor:
ObservableList<Item> items = FXCollections.observableArrayList(item ->
new Observable[] { item.checkedProperty() });
table.setItems(items);
This ensures that the list fires update notifications when the checkedProperty changes on any of its elements.
So now you can just do normal binding stuff like:
IntegerBinding numberChecked = Bindings.createIntegerBinding(() ->
(int) items.stream().filter(Item::isChecked).count(),
items);
button.visibleProperty().bind(numberChecked.greaterThanOrEqualTo(2));
If you want to be super-efficient:
int requiredNumberChecked = 2 ;
button.visibleProperty().bind(Bindings.createBooleanBinding(() ->
items.stream()
.filter(Item::isSelected)
.skip(requiredNumberChecked-1)
.findAny().isPresent(),
items));
(the binding will return true as soon as it finds two checked items, instead of scanning the entire list).

Dynamically Adding context menu items to tableview columns

I have the following controller that is instantiated many times on my gui. The reason is beacause it has a tableview that gets filled with different kind of data. Looks like this
class Controller {
#FXML
TableView<Map<String, String> myTable;
private Manager manager;
//Each TableView has different ammount of columns with different names that get dynamically added to the table view using this function
public void setUpColumns(List<TableColumn<Map<String, String>, String>> columns){
myTable.getColumns().addAll(columns);
addContextMenuToColumnHeaders();
}
private addContextMenuToColumnHeaders(){
for (TableColumn<Map<String, String>, ?> tc : myTable.getColumns()){
ContextMenu addToGraphContextMenu = createAddToGraphContextMenu(tc);
tc.setContextMenu(addToGraphContextMenu);
}
}
private ContextMenu createAddToGraphContextMenu(TableColumn<Map<String, String> String> tc){
for (MangerHandledObject mHO : manager.getHandledObjects()){
MenuItem menuItem = new MenuItem(mHO.getName());
menuItem.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event){
//I want each menu item to have access to the column that is added to get the name of the column. Even after dynamically adding new menuItems
manager.callMethod(tc.getName());
}
});
}
}
}
The manager handled objects are not static. So the are added and deleted from the list that the manager keeps. I tried this
contextMenu.setOnShowing(......)
and before showing it will always check for the list from the manager and re-make the context menu items. But the problems is that when this executes I don't have access to the columns anymore. Is there any way to bypass this? Should I implement my own context menu to have a field of the column Name?
It worked. But I had to add at least one dummy MenuItem on my context menu in order for it to appear.

Resources