跨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数据,避免直接操作对方的控件:
- 修改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; }
- 修改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); } } });
- 修改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
相关产品推荐
相关产品推荐

