将标签 textProperty 绑定到另一个最终 ObjectProperty 持有的对象 属性

Binding labels textProperty to object's property held by another final ObjectProperty

在我正在构建的应用程序中,我在这里使用了 James_D 提供的数据模型:

我可以找到一种方法将标签文本绑定到 DataModel

中保存的对象的 属性

数据结构如下: 模特class学生

//partial class
public class Student {

private final StringProperty displayName = new SimpleStringProperty();

public final StringProperty displayNameProperty(){
    return this.displayName;
}

public Student(){

}

public final String getDisplayName() {
    return this.displayNameProperty().get();
}

public final void setDisplayName(String displayName) {
    this.displayNameProperty().set(displayName);
}
}

学生实例由 StudentDataModel 持有class

public class StudentDataModel {
// complete student list
private final ObservableList<Student> studentList = FXCollections.observableArrayList();
private final ObjectProperty<Student> selectedStudent = new SimpleObjectProperty<>(new Student());
public final Student getSelectedStudent() {
     return selectedStudent.get();
}

public final ObjectProperty<Student> selectedStudentProperty() {
     return selectedStudent;
}

 public final void setSelectedStudent(Student student) {
     selectedStudent.set(student);
}
}

StudentList 由 Table 视图显示,有设置 selectedStudent 的更改监听器,如下所示:

    public class TableViewController {
    public void initModel(StudentDataModel studentDM) {
    // ensure model is set once
    if (this.studentDM != null) {
        throw new IllegalStateException("StudentDataModel can only be initialized once");
    }

    this.studentDM = studentDM;
    tableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
        if (newSelection != null) {
            studentDM.setSelectedStudent(newSelection);
        }
    });
   }}

还有另一个控制器 ActionsBarController 有标签来显示选定的学生(这似乎是多余的,但有选择多个对象来执行批量操作的选项)。 StudentDataModel 已正确初始化(我可以在调试器中看到它)但下面没有执行任何操作:

chosenStudentLabel.textProperty().bind(studentDM.getSelectedStudent().displayNameProperty());

//this shows class name with instance number changing correctly
chosenStudentLabel.textProperty().bind(studentDM.selectedStudentProperty().asString());

我可以将 ActionsBarController 注入 TableViewController 并从那里的更改监听器更改标签文本,但这似乎与数据模型适得其反。 我做错了什么?

您的代码不起作用,因为您在创建绑定时(即初始化模型时)调用(并计算)getSelectedStudent()。因此,您只绑定到当时选择的学生的 displayName 属性。 (如果未选择任何内容,您将获得 NullPointerException。)绑定只会在最初选择的学生的显示名称发生变化时发生变化;如果选择更改,它不会更改。

您需要一个绑定,当所选学生更改时,该绑定可与旧所选学生的显示名称解除绑定,并绑定到新所选学生的显示名称。一种方法是:

chosenStudentLabel.textProperty().bind(new StringBinding() {
    {
        studentDM.selectedStudentProperty().addListener((obs, oldStudent, newStudent) -> {
            if (oldStudent != null) {
                unbind(oldStudent.displayNameProperty());
            }
            if (newStudent != null) {
                bind(newStudent.displayNameProperty());
            }
            invalidate();
        });
    }
    @Override
    protected String computeValue() {
        if (studentDM.getSelectedStudent() == null) {
            return "" ;
        }
        return studentDM.getSelectedStudent().getDisplayName();
    }
});

请注意,还有一种“内置”方法可以执行此操作,但由于几个原因,它有点不能令人满意(在我看来)。首先,它依赖于将“嵌套 属性”的名称指定为 String,使用反射来访问它。这是不可取的,因为它无法在编译时检查 属性 是否存在,它需要打开模块进行访问,而且在性能方面不太好。其次,如果“链”中的一个属性为空(例如,在这种情况下,如果所选学生为空,这将是最初的情况),它会发出虚假警告,即使根据文档这是受支持的情况。但是,代码明显少了:

chosenStudentLabel.textProperty().bind(
    Bindings.selectString(studentDM.selectedStudentProperty(), "displayName")
);