如何在javafx中传递参数到辅助窗口?是否有与相应控制器通信的方法?

例如: 用户从TableView中选择一个客户,然后打开一个新窗口,显示该客户的信息。

Stage newStage = new Stage();
try 
{
    AnchorPane page = (AnchorPane) FXMLLoader.load(HectorGestion.class.getResource(fxmlResource));
    Scene scene = new Scene(page);
    newStage.setScene(scene);
    newStage.setTitle(windowTitle);
    newStage.setResizable(isResizable);
    if(showRightAway) 
    {
        newStage.show();
    }
}

newStage将是新窗口。问题是,我找不到一种方法告诉控制器在哪里寻找客户的信息(通过传递id作为参数)。

什么好主意吗?


当前回答

你可以决定使用一个公共可观察对象列表来存储公共数据,或者只是创建一个公共setter方法来存储数据并从相应的控制器中检索

其他回答

node类有一对方法 setUserData(对象) 而且 对象getUserData ()

您可以使用它将您的信息添加到Node。

因此,你可以调用page.setUserData(info);

如果设置了info,控制器可以检查。此外,如果需要,还可以使用ObjectProperty进行前后数据传输。

请看下面的文档: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html 在第一个版本中,handleButtonAction()被标记为@FXML,以允许控制器文档中定义的标记调用它。在第二个示例中,button字段被注释以允许加载器设置它的值。initialize()方法也有类似的注释。”

因此,您需要将控制器与节点关联起来,并将用户数据设置为节点。

使用MVC

这个回答主要集中在将参数从调用类传递给控制器的直接调用上。

如果相反,你想要解耦调用者和控制器,并使用一个更通用的架构,包括一个具有可设置和可侦听属性的模型类来实现控制器之间的通信,请参阅以下基本概述:

使用JavaFx应用MVC

推荐的方法

这个答案列举了向FXML控制器传递参数的不同机制。

对于小型应用程序,我强烈建议直接将参数从调用者传递给控制器——这很简单,直接,不需要额外的框架。

对于更大、更复杂的应用程序,如果您想在应用程序中使用依赖注入或事件总线机制,那么值得研究一下。

直接从调用者向控制器传递参数

通过从FXML加载器实例检索控制器并调用控制器上的方法以使用所需的数据值初始化控制器,将自定义数据传递给FXML控制器。

类似于下面的代码:

public Stage showCustomerDialog(Customer customer) {
  FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
      "customerDialog.fxml"
    )
  );

  Stage stage = new Stage(StageStyle.DECORATED);
  stage.setScene(
    new Scene(loader.load())
  );

  CustomerDialogController controller = loader.getController();
  controller.initData(customer);

  stage.show();

  return stage;
}

...

class CustomerDialogController {
  @FXML private Label customerName;
  void initialize() {}
  void initData(Customer customer) {
    customerName.setText(customer.getName());
  }
}

一个新的FXMLLoader被构造,如示例代码所示,即new FXMLLoader(location)。该位置是一个URL,您可以通过以下方式从FXML资源生成这样一个URL:

new FXMLLoader(getClass().getResource("sample.fxml"));

注意不要在FXMLLoader上使用静态加载函数,否则您将无法从加载器实例中获得控制器。

FXMLLoader实例本身从来不知道任何关于域对象的信息。你不直接传递应用程序特定的域对象到FXMLLoader构造函数,而是:

在指定位置根据fxml标记构造一个FXMLLoader 从FXMLLoader实例获取一个控制器。 调用检索到的控制器上的方法,为控制器提供对域对象的引用。

这篇博客(由另一位作者撰写)提供了一个类似的替代例子。

在FXMLLoader上设置控制器

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = loader.load();

你可以在代码中构造一个新的控制器,把你想从调用者那里得到的任何参数传递给控制器构造函数。一旦构建了控制器,就可以在调用load()实例方法之前在FXMLLoader实例上设置它。

要在加载器上设置控制器(在JavaFX 2.x中),你不能在你的fxml文件中定义fx:controller属性。

由于FXML中fx:controller定义的限制,我个人更喜欢从FXMLLoader中获取控制器,而不是将控制器设置到FXMLLoader中。

让控制器从外部静态方法检索参数

Sergey在Controller.java文件中对Javafx 2.0 How-to Application.getParameters()的回答举例说明了这种方法。

使用依赖注入

FXMLLoader支持依赖注入系统,如Guice, Spring或Java EE CDI,允许你在FXMLLoader上设置自定义控制器工厂。这提供了一个回调,您可以使用该回调创建控制器实例,其中包含由各自的依赖注入系统注入的依赖值。

下面给出了一个JavaFX应用程序和控制器依赖注入Spring的示例:

在JavaFX中添加Spring依赖注入(JPA Repo, Service)

加力是一种非常好的、干净的依赖注入方法。Fx框架与示例air-hacks应用程序使用它。加力燃烧室。fx依赖于JEE6 javax。注入以执行依赖项注入。

使用事件总线

Greg Brown是FXML规范的最初创建者和实现者,他经常建议考虑使用事件总线,例如Guava EventBus,用于FXML实例化控制器和其他应用程序逻辑之间的通信。

EventBus是一个简单但功能强大的带有注释的发布/订阅API,它允许pojo在JVM中的任何地方相互通信,而不必相互引用。

后续的问答

关于第一种方法,你为什么返回舞台?这个方法也可以是空的,因为你已经给出了show()命令;在返回阶段之前;。如何通过返回Stage来计划使用

It is a functional solution to a problem. A stage is returned from the showCustomerDialog function so that a reference to it can be stored by an external class which may wish to do something, such as hide the stage based on a button click in the main window, at a later time. An alternate, object-oriented solution could encapsulate the functionality and stage reference inside a CustomerDialog object or have a CustomerDialog extend Stage. A full example for an object-oriented interface to a custom dialog encapsulating FXML, controller and model data is beyond the scope of this answer, but may make a worthwhile blog post for anybody inclined to create one.


StackOverflow用户@ zim提供的附加信息

Spring引导依赖注入示例

关于如何做到“Spring Boot Way”的问题,有一个关于JavaFX 2的讨论,我在附件中回答了这个问题。 该方法仍然有效,并在2016年3月Spring Boot v1.3.3.RELEASE上进行了测试: https://stackoverflow.com/a/36310391/1281217


有时,你可能想要将结果返回给调用者,在这种情况下,你可以检查出相关问题的答案:

JavaFX FXML从控制器A传递到控制器B并返回的参数

下面是一个使用Guice注入的控制器的例子。

/**
 * Loads a FXML file and injects its controller from the given Guice {@code Provider}
 */
public abstract class GuiceFxmlLoader {

   public GuiceFxmlLoader(Stage stage, Provider<?> provider) {
      mStage = Objects.requireNonNull(stage);
      mProvider = Objects.requireNonNull(provider);
   }

   /**
    * @return the FXML file name
    */
   public abstract String getFileName();

   /**
    * Load FXML, set its controller with given {@code Provider}, and add it to {@code Stage}.
    */
   public void loadView() {
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(getFileName()));
         loader.setControllerFactory(p -> mProvider.get());
         Node view = loader.load();
         setViewInStage(view);
      }
      catch (IOException ex) {
         LOGGER.error("Failed to load FXML: " + getFileName(), ex);
      }
   }

   private void setViewInStage(Node view) {
      BorderPane pane = (BorderPane)mStage.getScene().getRoot();
      pane.setCenter(view);
   }

   private static final Logger LOGGER = Logger.getLogger(GuiceFxmlLoader.class);

   private final Stage mStage;
   private final Provider<?> mProvider;
}

下面是加载器的具体实现:

public class ConcreteViewLoader extends GuiceFxmlLoader {

   @Inject
   public ConcreteViewLoader(Stage stage, Provider<MyController> provider) {
      super(stage, provider);
   }

   @Override
   public String getFileName() {
      return "my_view.fxml";
   }
}

注意这个例子将视图加载到BoarderPane的中心,BoarderPane是Stage中Scene的根。这与示例(我的特定用例的实现细节)无关,但决定保留它,因为有些人可能会发现它有用。

是的,你可以。 你需要添加第一个控制器:

YourController controller = loader.getController();     
controller.setclient(client);

然后在第二个语句中声明一个客户端,然后在控制器的底部:

public void setclien(Client c) {
    this.client = c;
}

下面是一个通过命名空间将参数传递给fxml文档的示例。

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
    <BorderPane>
        <center>
            <Label text="$labelText"/>
        </center>
    </BorderPane>
</VBox>

为命名空间变量labelText定义值External Text:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class NamespaceParameterExampleApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("namespace-parameter-example.fxml"));

        fxmlLoader.getNamespace()
                  .put("labelText", "External Text");

        final Parent root = fxmlLoader.load();

        primaryStage.setTitle("Namespace Parameter Example");
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }
}