javafx tableview updates data only after sorting


Question

I have two javafx tableviews. If I change a value in the first one, the second one shows the changes only after sorting (although the model is updated).

My entities

package Entity;

import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;

public class Arbeitgeber {

private SimpleIntegerProperty id = new SimpleIntegerProperty();
private SimpleStringProperty name = new SimpleStringProperty();

public int getId() {
    return id.get();
}

public void setId(int id) {
    this.id.set(id);
}
...


package Entity;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;

public class Person {

private SimpleIntegerProperty id = new SimpleIntegerProperty();
private SimpleStringProperty name = new SimpleStringProperty();
private SimpleStringProperty gruppe = new SimpleStringProperty();
private ObjectProperty<Arbeitgeber> arbeitgeber = new SimpleObjectProperty<>();

public int getId() {
    return id.get();
}

public void setId(int id) {
    this.id.set(id);
}
...

My testcase :

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import Entity.Arbeitgeber;
import Entity.Person;


public class TableviewTest extends Application {

private TableView<Arbeitgeber> arbeitgeberTable;
private TableView<Person> personTable;

ObservableList<String> gruppeOptions = 
    FXCollections.observableArrayList(
        "Arbeiter",
        "Angestellter",
        "Leiter"
    );

private ObservableList<Arbeitgeber> arbeitgeberListe = FXCollections.observableArrayList(new Arbeitgeber(1, "Goldgräber Inc."), new Arbeitgeber(2, "Hamster GmbH"), new Arbeitgeber(3, "Wissen"));
private ObservableList<Person> personen = FXCollections.observableArrayList(
        new Person(1, "Max Musterman", gruppeOptions.get(1), arbeitgeberListe.get(0)), 
        new Person(2, "Bernd Brot", gruppeOptions.get(0), arbeitgeberListe.get(0)), 
        new Person(3, "Fred Feuer", gruppeOptions.get(2), arbeitgeberListe.get(1)), 
        new Person(4, "Uli Stein", gruppeOptions.get(1), arbeitgeberListe.get(2)));

@Override
public void start(Stage primaryStage) throws Exception {
    createArbeitgeberTableview();
    createPersonTableview();

    VBox vbox = new VBox(15);

    Label aLabel = new Label("Arbeitgeber");
    Label pLabel = new Label("Personen");
    vbox.getChildren().addAll(aLabel, arbeitgeberTable, pLabel, personTable);

    Scene scene = new Scene(vbox);
    primaryStage.setScene(scene);

    primaryStage.show();
}

/**
 * @param args
 */
public static void main(String[] args) {
    launch(args);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private TableView<Arbeitgeber> createArbeitgeberTableview() {
    arbeitgeberTable = new TableView<>(arbeitgeberListe);

    arbeitgeberTable.setEditable(true);

    TableColumn<Arbeitgeber, Integer> idCol = new TableColumn<>("ID");
    TableColumn nameCol = new TableColumn<>("Name");

    idCol.setCellValueFactory(new PropertyValueFactory<Arbeitgeber, Integer>("id"));
    nameCol.setCellValueFactory(new PropertyValueFactory<Arbeitgeber, String>("name"));


    nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
    nameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Arbeitgeber,String>>() {

        @Override
        public void handle(CellEditEvent<Arbeitgeber, String> event) {
            ((Arbeitgeber) event.getTableView().getItems().get(event.getTablePosition().getRow())).setName(event.getNewValue());                

            for(Person p : personTable.getItems()) {    
                System.out.println(p.getArbeitgeber()); //shows the new value
            }
        }
    });

    arbeitgeberTable.getColumns().addAll(idCol, nameCol);
    arbeitgeberTable.setTableMenuButtonVisible(true);
    return arbeitgeberTable;
}

@SuppressWarnings("unchecked")
private TableView<Person> createPersonTableview(){
    personTable = new TableView<>(personen);

    TableColumn<Person, Integer> idCol = new TableColumn<>("ID");
    TableColumn<Person, String> nameCol = new TableColumn<>("Name");
    TableColumn<Person, String> gruppeCol = new TableColumn<>("Gruppe");
    TableColumn<Person, Arbeitgeber> arbCol = new TableColumn<>("Arbeitgeber");

    idCol.setCellValueFactory(new PropertyValueFactory<Person, Integer>("id"));
    nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
    gruppeCol.setCellValueFactory(new PropertyValueFactory<Person, String>("gruppe"));
    arbCol.setCellValueFactory(new PropertyValueFactory<Person, Arbeitgeber>("arbeitgeber"));

    personTable.getColumns().addAll(idCol, nameCol, gruppeCol, arbCol);
    personTable.setTableMenuButtonVisible(true);
    return personTable;
}
}

I tried something like :

for(TableColumn t : personTable.getColumns()){
  t.setVisible(false);
  t.setVisible(true);
}

but it does not work. I would like to solve it without sorting. Does anyone have an idea?

Thanks.

1
2
7/8/2016 11:05:37 AM

Accepted Answer

Problem Restatement

As I understand it you have the following situation.

  1. A person has a reference to an arbeitgeber
  2. You display the arbeitgeber in one table and the person in another table.
  3. The person table has an Arbeitgeber column which provides information on the related arbeitgeber.
  4. In the arbeitgeber, the name of the arbeitgeber is editable.
  5. When you edit arbeitgeber name, in the arbeitgeber table, you want the name update for the arbeitgeber to be automatically reflected in your person table.

Solution Approach

  1. Ensure that entity objects have property access methods so that the values of the individual entity fields can be observed for changes and appropriate action taken.

    public StringProperty nameProperty() {
        return name;
    }
    
  2. Set up a binding between the cell values of the Arbeitgeber column in the person table and the arbeitgeber name.

    In Java 7:

    arbCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
        public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
            return Bindings.selectString(
                new ReadOnlyObjectWrapper<Object>(
                    p.getValue()
                ), 
                "arbeitgeber", "name"
            );
        }
    });
    

    In Java 8:

    arbCol.setCellValueFactory(p -> 
        Bindings.selectString(p.getValue(), "arbeitgeber", "name")
    );
    

Sample Output

Before and after edit snapshots:

unedited   edited

Sample code

TableViewTest.java

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TableViewTest extends Application {

    private TableView<Arbeitgeber> arbeitgeberTable;
    private TableView<Person> personTable;

    ObservableList<String> gruppeOptions =
        FXCollections.observableArrayList(
                "Arbeiter",
                "Angestellter",
                "Leiter"
        );

    private ObservableList<Arbeitgeber> arbeitgeberListe = FXCollections.observableArrayList(new Arbeitgeber(1, "Goldgräber Inc."), new Arbeitgeber(2, "Hamster GmbH"), new Arbeitgeber(3, "Wissen"));
    private ObservableList<Person> personen = FXCollections.observableArrayList(
            new Person(1, "Max Musterman", gruppeOptions.get(1), arbeitgeberListe.get(0)),
            new Person(2, "Bernd Brot", gruppeOptions.get(0), arbeitgeberListe.get(0)),
            new Person(3, "Fred Feuer", gruppeOptions.get(2), arbeitgeberListe.get(1)),
            new Person(4, "Uli Stein", gruppeOptions.get(1), arbeitgeberListe.get(2)));

    @Override
    public void start(Stage primaryStage) throws Exception {
        createArbeitgeberTableview();
        createPersonTableview();

        VBox vbox = new VBox(15);

        Label aLabel = new Label("Arbeitgeber");
        Label pLabel = new Label("Personen");
        vbox.getChildren().addAll(aLabel, arbeitgeberTable, pLabel, personTable);

        Scene scene = new Scene(vbox);
        primaryStage.setScene(scene);

        primaryStage.show();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        launch(args);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private TableView<Arbeitgeber> createArbeitgeberTableview() {
        arbeitgeberTable = new TableView<>(arbeitgeberListe);

        arbeitgeberTable.setEditable(true);

        TableColumn<Arbeitgeber, Integer> idCol = new TableColumn<>("ID");
        TableColumn nameCol = new TableColumn<>("Name");

        idCol.setCellValueFactory(new PropertyValueFactory<Arbeitgeber, Integer>("id"));
        nameCol.setCellValueFactory(new PropertyValueFactory<Arbeitgeber, String>("name"));


        nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
        nameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Arbeitgeber,String>>() {

            @Override
            public void handle(TableColumn.CellEditEvent<Arbeitgeber, String> event) {
                ((Arbeitgeber) event.getTableView().getItems().get(event.getTablePosition().getRow())).setName(event.getNewValue());

                for(Person p : personTable.getItems()) {
                    System.out.println(p.getArbeitgeber()); //shows the new value
                }
            }
        });

        arbeitgeberTable.getColumns().addAll(idCol, nameCol);
        arbeitgeberTable.setTableMenuButtonVisible(true);
        return arbeitgeberTable;
    }

    @SuppressWarnings("unchecked")
    private TableView<Person> createPersonTableview(){
        personTable = new TableView<>(personen);

        TableColumn<Person, Integer> idCol = new TableColumn<>("ID");
        TableColumn<Person, String> nameCol = new TableColumn<>("Name");
        TableColumn<Person, String> gruppeCol = new TableColumn<>("Gruppe");
        TableColumn<Person, String> arbCol = new TableColumn<>("Arbeitgeber");

        idCol.setCellValueFactory(new PropertyValueFactory<Person, Integer>("id"));
        nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
        gruppeCol.setCellValueFactory(new PropertyValueFactory<Person, String>("gruppe"));

          arbCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
              public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
                  return Bindings.selectString(
                      new ReadOnlyObjectWrapper<Object>(
                          p.getValue()
                      ), 
                     "arbeitgeber", "name"
                  );
              }
          });

        personTable.getColumns().addAll(idCol, nameCol, gruppeCol, arbCol);
        personTable.setTableMenuButtonVisible(true);
        return personTable;
    }
}

Person.java

import javafx.beans.property.*;

public class Person {

    private IntegerProperty id = new SimpleIntegerProperty();
    private StringProperty name = new SimpleStringProperty();
    private StringProperty gruppe = new SimpleStringProperty();
    private ObjectProperty<Arbeitgeber> arbeitgeber = new SimpleObjectProperty<>();

    public Person(int id, String name, String gruppe, Arbeitgeber arbeitgeber) {
        setId(id);
        setName(name);
        setGruppe(gruppe);
        setArbeitgeber(arbeitgeber);
    }

    public int getId() {
        return id.get();
    }

    public IntegerProperty idProperty() {
        return id;
    }

    public void setId(int id) {
        this.id.set(id);
    }

    public String getName() {
        return name.get();
    }

    public StringProperty nameProperty() {
        return name;
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public String getGruppe() {
        return gruppe.get();
    }

    public StringProperty gruppeProperty() {
        return gruppe;
    }

    public void setGruppe(String gruppe) {
        this.gruppe.set(gruppe);
    }

    public Arbeitgeber getArbeitgeber() {
        return arbeitgeber.get();
    }

    public ObjectProperty<Arbeitgeber> arbeitgeberProperty() {
        return arbeitgeber;
    }

    public void setArbeitgeber(Arbeitgeber arbeitgeber) {
        this.arbeitgeber.set(arbeitgeber);
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name=" + name +
                ", gruppe=" + gruppe +
                ", arbeitgeber=" + arbeitgeber +
                '}';
    }
}

Arbeitgeber.java

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Arbeitgeber {
    private IntegerProperty id = new SimpleIntegerProperty();
    private StringProperty name = new SimpleStringProperty();

    public Arbeitgeber(int id, String name) {
        setId(id);
        setName(name);
    }

    public IntegerProperty idProperty() {
        return id;
    }

    public int getId() {
        return id.get();
    }

    public void setId(int id) {
        this.id.set(id);
    }

    public StringProperty nameProperty() {
        return name;
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    @Override
    public String toString() {
        return "Arbeitgeber{" +
                "id=" + id +
                ", name=" + name +
                '}';
    }
}
2
6/11/2013 9:05:39 PM

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon