You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨FXML/Controller更新JavaFX TableView的问题咨询

问题:FXML添加手术时TableView刷新失败,控制器引用为空

我创建了两个FXML文件:一个用于展示患者手术列表(通过TableView),另一个是添加手术的弹窗窗口。用户右键点击TableView会弹出上下文菜单,选择“Add Surgery”后打开弹窗,填写信息后点击保存,期望TableView自动刷新显示新手术,但点击保存时出现错误:

Cannot read field "surgeryTableView" because "this.patientModuleController" is null

现有代码

PatientModuleController.java

@FXML
public TableView<ISurgeryModel> surgeryTableView;


ObservableList<ISurgeryModel> initialPTSurgData(){
    ISurgeryModel ptSur1 = new ISurgeryModel("10/20/2023", "Other", "L", "surgeon", "facility", "comments");
    return FXCollections.observableArrayList(ptSur1);
}


@Override
public void initialize(URL PatientModule, ResourceBundle resourceBundle) {
    surgeryDate.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgeryDate")));
    surgeryProcedure.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgeryProcedure")));
    surgerySide.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgerySide")));
    surgeryTableView.setItems(initialPTSurgData());
    ContextMenu surgContext = new ContextMenu();
    MenuItem addSurgery = new MenuItem("Add Surgery");
    MenuItem viewSurgDetails = new MenuItem("View Surgery Details");
    MenuItem deleteSurg = new MenuItem("Delete Surgery");
    surgContext.getItems().add(addSurgery);
    surgContext.getItems().add(viewSurgDetails);
    surgContext.getItems().add(deleteSurg);
    surgeryTableView.setContextMenu(surgContext);

    addSurgery.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            try {
                Parent popUp;
                popUp = FXMLLoader.load(Objects.requireNonNull(GaitApplication.class.getResource("Wizards/AddSurgery.fxml")));
                Stage stage1 = new Stage();
                stage1.setTitle("Add Surgery:   ");
                stage1.setScene(new Scene(popUp, 600, 480));
                stage1.show();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    });
}

AddSurgeryController.java

@FXML
private PatientModuleController patientModuleController;

@FXML
public void onSaveSurgery(ActionEvent event){
    ISurgeryModel newData = new ISurgeryModel(date.getText(), procedure.getText(), side.getText(), surgeon.getText(), facility.getText(), comments.getText());
    patientModuleController.surgeryTableView.getItems().add(newData);
    this.addSurgery.getScene().getWindow().hide();
}

解决方案

不需要给AddSurgeryController创建构造方法,问题根源是FXML不会自动关联两个独立控制器的引用,patientModuleController变量始终为null。下面提供两种可行的修复方式:

方法一:手动传递控制器引用

修改PatientModuleController中打开弹窗的代码,手动获取AddSurgeryController实例并传递当前控制器:

addSurgery.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        try {
            FXMLLoader loader = new FXMLLoader(GaitApplication.class.getResource("Wizards/AddSurgery.fxml"));
            Parent popUp = loader.load();
            // 获取弹窗控制器实例
            AddSurgeryController addController = loader.getController();
            // 传递当前控制器引用
            addController.setPatientModuleController(PatientModuleController.this);
            
            Stage stage1 = new Stage();
            stage1.setTitle("Add Surgery:   ");
            stage1.setScene(new Scene(popUp, 600, 480));
            stage1.show();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
});

然后修改AddSurgeryController,去掉patientModuleController的@FXML注解,添加setter方法:

// 移除@FXML,因为该变量不是FXML文件中定义的控件
private PatientModuleController patientModuleController;

// 添加setter方法用于接收控制器引用
public void setPatientModuleController(PatientModuleController controller) {
    this.patientModuleController = controller;
}

@FXML
public void onSaveSurgery(ActionEvent event){
    ISurgeryModel newData = new ISurgeryModel(date.getText(), procedure.getText(), side.getText(), surgeon.getText(), facility.getText(), comments.getText());
    patientModuleController.surgeryTableView.getItems().add(newData);
    this.addSurgery.getScene().getWindow().hide();
}

方法二:共享数据模型(更推荐)

遵循MVC思想,让两个控制器共享同一个ObservableList数据,避免直接操作对方的控件:

  1. 修改PatientModuleController,将数据列表改为类成员变量并提供获取方法:
private ObservableList<ISurgeryModel> surgeryData;

@Override
public void initialize(URL PatientModule, ResourceBundle resourceBundle) {
    surgeryDate.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgeryDate")));
    surgeryProcedure.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgeryProcedure")));
    surgerySide.setCellValueFactory((new PropertyValueFactory<ISurgeryModel, String>("surgerySide")));
    
    // 初始化数据列表并绑定到TableView
    surgeryData = initialPTSurgData();
    surgeryTableView.setItems(surgeryData);
    
    // 上下文菜单和弹窗代码不变...
}

// 提供获取数据列表的方法
public ObservableList<ISurgeryModel> getSurgeryData() {
    return surgeryData;
}
  1. 修改PatientModuleController中打开弹窗的代码,传递共享数据列表:
addSurgery.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        try {
            FXMLLoader loader = new FXMLLoader(GaitApplication.class.getResource("Wizards/AddSurgery.fxml"));
            Parent popUp = loader.load();
            AddSurgeryController addController = loader.getController();
            // 传递共享数据列表
            addController.setSurgeryData(surgeryData);
            
            Stage stage1 = new Stage();
            stage1.setTitle("Add Surgery:   ");
            stage1.setScene(new Scene(popUp, 600, 480));
            stage1.show();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
});
  1. 修改AddSurgeryController,通过共享列表添加数据:
private ObservableList<ISurgeryModel> surgeryData;

// 添加setter方法接收共享数据列表
public void setSurgeryData(ObservableList<ISurgeryModel> data) {
    this.surgeryData = data;
}

@FXML
public void onSaveSurgery(ActionEvent event){
    ISurgeryModel newData = new ISurgeryModel(date.getText(), procedure.getText(), side.getText(), surgeon.getText(), facility.getText(), comments.getText());
    // 直接添加到共享列表,TableView会自动刷新
    surgeryData.add(newData);
    this.addSurgery.getScene().getWindow().hide();
}

这种方式解耦了两个控制器的依赖,代码更易维护和扩展。

内容的提问来源于stack exchange,提问作者Kameron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 09:40:54