如果嵌套对象绑定发生更改,如何更新JavaFX TableView?

时间:2017-02-26 13:57:32

标签: javafx tableview nested-properties

我有一个ObservableList<Member>成员,我想在TableView<Member>中显示。

Member类由Person(以及其他不重要的值组成,因为它们不会出现在tableview中)。每个Person都有一个StringProperty“名称”和一个ObjectProperty<Dog>“狗”。此外,每只狗都有一个StringProperty“名称”。

tableview应显示第一列中人物的名称和第二列中狗的名称。像这样:Example table view

我设法通过将TableColumn<Member, Person>绑定到member.personProperty()并使用自定义单元工厂显示人名或狗的名称来实现此目的。

现在,我还必须ComboBox es,用户可以通过该TableView更新valueProperty()所选行中的名称。因此,我在所选项目和组合框的Member之间创建了绑定。

如果通过组合框更改了表格视图,则表格视图会更新人员姓名的单元格,而狗名称的单元格不会自动显示新值。我知道这是因为package sample; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.*; import javafx.util.StringConverter; public class ControllerString { private class Dog { private final StringProperty name; public Dog(String name) { this.name = new SimpleStringProperty(name); } public String getName() { return name.get(); } public StringProperty nameProperty() { return name; } public void setName(String name) { this.name.set(name); } } private class Person { private final StringProperty name; private final ObjectProperty<Dog> dog; public Person(String name, Dog dog) { this.name = new SimpleStringProperty(name); this.dog = new SimpleObjectProperty<>(dog); } public String getName() { return name.get(); } public StringProperty nameProperty() { return name; } public void setName(String name) { this.name.set(name); } public Dog getDog() { return dog.get(); } public ObjectProperty<Dog> dogProperty() { return dog; } public void setDog(Dog dog) { this.dog.set(dog); } } private class Member { private final ObjectProperty<Person> person; public Member(Person person) { this.person = new SimpleObjectProperty<>(person); } public Person getPerson() { return person.get(); } public ObjectProperty<Person> personProperty() { return person; } public void setPerson(Person person) { this.person.set(person); } } @FXML private TableView<Member> membersTableView; @FXML private TableColumn<Member, Person> nameTableColumn; @FXML private TableColumn<Member, Person> dogTableColumn; @FXML private ComboBox<Person> nameComboBox; @FXML private ComboBox<Dog> dogComboBox; private final ObservableList<Member> members = FXCollections.observableArrayList(); @FXML private void initialize() { nameTableColumn.setCellValueFactory(cellData -> cellData.getValue().personProperty()); nameTableColumn.setCellFactory(column -> new TableCell<Member, Person>() { @Override protected void updateItem(Person person, boolean empty) { super.updateItem(person, empty); setContentDisplay(ContentDisplay.TEXT_ONLY); if(person == null || empty) { setText(null); } else { setText(person.getName()); } } }); dogTableColumn.setCellValueFactory(cellData -> cellData.getValue().personProperty()); dogTableColumn.setCellFactory(column -> new TableCell<Member, Person>() { @Override protected void updateItem(Person person, boolean empty) { super.updateItem(person, empty); setContentDisplay(ContentDisplay.TEXT_ONLY); if(person == null || empty) { setText(null); } else { setText(person.getDog().getName()); } } }); nameComboBox.setConverter(new StringConverter<Person>() { @Override public String toString(Person person) { if(person == null) { return null; } return person.getName(); } @Override public Person fromString(String name) { return new Person(name, new Dog("Puppy")); } }); dogComboBox.setConverter(new StringConverter<Dog>() { @Override public String toString(Dog dog) { if(dog == null) { return null; } return dog.getName(); } @Override public Dog fromString(String name) { return new Dog(name); } }); membersTableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Member>) change -> { while(change.next()) { if(change.wasRemoved()) { Member oldValue = change.getRemoved().get(0); nameComboBox.valueProperty().unbindBidirectional(oldValue.personProperty()); } if(change.wasAdded()) { Member newValue = change.getAddedSubList().get(0); nameComboBox.valueProperty().bindBidirectional(newValue.personProperty()); } } }); nameComboBox.valueProperty().addListener(((observable, oldValue, newValue) -> { if(oldValue != null) { dogComboBox.valueProperty().unbindBidirectional(oldValue.dogProperty()); } if(newValue != null) { dogComboBox.valueProperty().bindBidirectional(newValue.dogProperty()); } })); membersTableView.setItems(members); members.add(new Member(new Person("Bob", new Dog("Caesar")))); } } 没有得到关于dog属性更改的通知。但我还没有找到一个解决方案,如何让对象知道它的子对象中的变化。

总而言之,我的问题是(1)如何在平面表视图中显示嵌套对象,以及(2)如果任何(可能)嵌套属性发生更改,如何自动更新表视图。

示例代码:

<a href="assets/img/photo.png" data-featherlight="image" data-featherlight-closeOnClick= "background" >Open Image</a>

1 个答案:

答案 0 :(得分:2)

如果您将PersonDog的可见性修改为publicBindings.select可以与cellValueFactory一起使用,以便您摆脱自定义TableCell实现:

public class Dog {
    ...
}

public class Person {
    ...
}

...

@FXML
private TableColumn<Member, String> nameTableColumn;
@FXML
private TableColumn<Member, String> dogTableColumn;

...

@FXML
private void initialize() {
    nameTableColumn.setCellValueFactory(cellData -> Bindings.select(cellData.getValue().personProperty(), "name"));
    dogTableColumn.setCellValueFactory(cellData -> Bindings.select(cellData.getValue().personProperty(), "dog", "name"));
    ...

如果其中一个中间属性可以包含null,那么你可以预期这会引起很多警告......