Paramètres de passage JavaFX FXML

Comment passer des paramètres à une fenêtre secondaire dans javafx? Est-il un moyen de communiquer avec le contrôleur correspondant?

par exemple: L'utilisateur choisit un client à partir d'un TableView et une nouvelle fenêtre est ouverte, montrant les informations du client.

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 serait la nouvelle fenêtre. Le problème est que je n'arrive pas à trouver un moyen de dire au contrôleur où chercher les informations du client (en passant l'id comme paramètre).

des idées?

154
demandé sur RAnders00 2013-01-07 03:33:12

8 réponses

Approche Recommandée

cette réponse énumère différents mécanismes pour transmettre les paramètres aux contrôleurs FXML.

pour les petites applications je recommande fortement de passer les paramètres directement de l'appelant au contrôleur - c'est simple, simple et ne nécessite pas de cadres supplémentaires.

pour les applications plus grandes et plus compliquées, il serait intéressant d'étudier si vous voulez pour utiliser injection de dépendance ou Event Bus mécanismes dans votre application.

transmettre les paramètres directement de l'appelant au contrôleur

transmet des données personnalisées à un contrôleur FXML en récupérant le contrôleur de l'instance du chargeur FXML et en appelant une méthode sur le contrôleur pour l'initialiser avec les valeurs de données requises.

Quelque chose comme le code suivant:

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

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

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

  stage.show();

  return stage;
}

...

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

un nouveau chargeur FXML est construit comme indiqué dans le code d'échantillon, à savoir new FXMLLoader(location) . L'emplacement est une URL et vous pouvez générer une telle URL à partir d'une ressource FXML par:

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

faites attention de ne pas utiliser une fonction de charge statique sur le Fxmllloader, ou vous ne pourrez pas obtenir votre contrôleur à partir de l'instance de votre chargeur.

FXMLLoader instances eux-mêmes ne savent jamais rien sur les objets de domaine. Vous ne passez pas directement les objets du domaine spécifique à l'application dans le constructeur FXMLLoader, vous:

  1. construire un chargeur FXML basé sur un markup fxml à un endroit spécifié
  2. obtenir un contrôleur à partir de L'instance Fxmllloader.
  3. invoquer des méthodes sur le contrôleur récupéré pour fournir au contrôleur des références aux objets de domaine.

de Ce blog (par un autre écrivain) fournit une autre, mais semblable, exemple .

réglage D'un contrôleur sur le FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

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

Pane mainPane = (Pane) loader.load();

vous pouvez construire un nouveau contrôleur en code, en passant tous les paramètres que vous voulez de votre interlocuteur dans le constructeur du contrôleur. Une fois que vous avez construit un controller, vous pouvez le régler sur une instance Fxmllloader avant vous invoquez la méthode load() instance .

pour régler un contrôleur sur un chargeur (en JavaFX 2.x) vous ne pouvez pas non plus définir un attribut fx:controller dans votre fichier fxml.

en raison de la limitation de la définition de fx:controller dans FXML, je préfère personnellement obtenir le controller à partir du FXMLLoader plutôt que de configurer le controller dans le FXMLLoader.

dont le contrôleur récupère les paramètres d'une méthode statique externe

cette méthode est illustrée par la réponse de Sergey à Javafx 2.0 How-to Application.getParameters () dans un contrôleur.fichier java .

Use Injection De Dépendance

FXMLLoader prend en charge les systèmes d'injection de dépendances comme Guice, Spring ou Java EE CDI en vous permettant de définir un usine de contrôleur personnalisé sur le FXMLLoader. Ceci fournit un callback que vous pouvez utiliser pour créer l'instance du contrôleur avec des valeurs dépendantes injectées par le système d'injection de dépendances respectif. Il y a un exemple d'intégration de FXML avec le système D'injection de dépendance de ressort (malheureusement le lien est mort et le contenu est parti, si quelqu'un connaît un exemple similaire, s'il vous plaît éditez cette question pour y faire référence), bien qu'il soit un peu plus clunk que ce qu'il serait en utilisant les nouvelles fonctionnalités d'usine de contrôleur personnalisé disponibles dans JavaFX 2.2.

la postcombustion illustre une méthode d'injection de dépendance vraiment agréable et propre.fx cadre avec un échantillon air-hacks application qui l'utilise. postcombustion.fx s'appuie sur jee6 javax.injecter pour effectuer l'injection de dépendance.

utiliser un bus événementiel

Greg Brown, le créateur et l'exécuteur de la spécification FXML originale, suggère souvent d'envisager l'utilisation d'un event bus pour la communication entre les contrôleurs instantiated FXML et d'autres logiques d'application.

L'EventBus est une API simple mais puissante de publication/Abonnement avec annotations qui permet aux POJOs de communiquer entre eux n'importe où dans une JVM sans avoir à se référer les uns aux autres.

questions et réponses de suivi

sur la première méthode, pourquoi pensez-vous du retour de la Scène? La méthode peut être vide aussi bien parce que vous donnez déjà la commande show(); juste avant l'étape de retour;. Comment planifier l'utilisation en retournant la scène

il s'agit d'une solution fonctionnelle à un problème. Une étape est renvoyée de la fonction showCustomerDialog de sorte qu'une référence à celle-ci puisse être stockée par un classe externe qui souhaitent faire quelque chose, comme cacher la scène basée sur un bouton, cliquez sur dans la fenêtre principale, à une date ultérieure. Une solution alternative orientée objet pourrait encapsuler la fonctionnalité et la référence d'étape à l'intérieur d'un objet CustomerDialog ou avoir une étape CustomerDialog extend. Un exemple complet pour une interface orientée objet à un dialogue personnalisé encapsulant FXML, contrôleur et données de modèle est au-delà de la portée de cette réponse, mais peut faire un billet de blog valable pour n'importe qui enclin à en créer un.


informations supplémentaires fournies par L'utilisateur StackOverflow nommé @dzim

exemple pour Injection de dépendance de Botte de ressort

la question de savoir comment le faire "The Spring Boot Way", il y a eu une discussion sur JavaFX 2, que j'ai répondu dans le permalink ci-joint. L'approche est toujours valide et testé en mars 2016, sur Spring Boot v1.3.3.PUBLIER: https://stackoverflow.com/a/36310391/1281217


Parfois, vous pouvez transmettre les résultats à l'appelant, dans ce cas, vous pouvez consulter la réponse à la question:

220
répondu jewelsea 2018-01-11 23:18:21

javafx.scène.La classe de noeud a une paire de méthodes setUserData(objet) et Objet getUserData ()

que vous pouvez utiliser pour ajouter vos informations au noeud.

donc, vous pouvez appeler page.setUserData (info);

et le contrôleur peut vérifier, si l'information est définie. De plus, vous pouvez utiliser ObjectProperty pour le transfert de données, si nécessaire.

observez une documentation ici: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html Avant le membre de phrase "Dans la première version, la handleButtonAction() est taggés avec @FXML pour permettre le balisage définie dans le contrôleur du document pour l'appeler. Dans le deuxième exemple, le champ bouton est annoté pour permettre au chargeur de définir sa valeur. La méthode initialize () est annotée de la même façon."

ainsi, vous devez associer un contrôleur avec un noeud, et mettre des données d'utilisateur à la nœud.

8
répondu Alexander Kirov 2013-01-07 00:02:20

voici un exemple pour passer des paramètres à un document fxml à travers l'espace de noms.

<?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>

la définition de la valeur External Text pour les noms de variable labelText :

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();
    }
}
6
répondu user1503636 2016-01-12 04:05:07

cela fonctionne ..

rappelez-vous que la première fois que vous imprimez la valeur de passage vous obtiendrez null, Vous pouvez l'utiliser après avoir chargé windows , même chose pour tout ce que vous voulez coder pour n'importe quel autre composant.

Premier Contrôleur

try {
                                Stage st = new Stage();
                                 FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/inty360/free/form/MainOnline.fxml"));

                                Parent sceneMain = loader.load();

                                MainOnlineController controller = loader.<MainOnlineController>getController();
                                controller.initVariable(99L);

                                Scene scene = new Scene(sceneMain);
                                st.setScene(scene);
                                st.setMaximized(true);
                                st.setTitle("My App");
                                st.show();
                            } catch (IOException ex) {
                                Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
                            }

Un Autre Contrôleur

public void initVariable(Long id_usuario){
        this.id_usuario = id_usuario;
        label_usuario_nombre.setText(id_usuario.toString());

    }
3
répondu diego matos - keke 2016-04-28 22:25:12

je me rends compte que c'est un très vieux poste et a déjà quelques grandes réponses, mais j'ai voulu faire un simple MCVE pour montrer une telle approche et permettre à de nouveaux codeurs un moyen de voir rapidement le concept en action.

Dans cet exemple, nous allons utiliser 5 fichiers:

  1. Principal.java - utilisé simplement pour démarrer l'application et appeler le premier contrôleur.
  2. Controller1.java - le contrôleur de la première mise en page FXML.
  3. Controller2.java - le contrôleur pour la deuxième mise en page FXML.
  4. Layout1.fxml - la mise en page FXML pour la première scène.
  5. Layout2.fxml - la mise en page FXML pour la deuxième scène.

tous les fichiers sont repris dans leur intégralité en bas de ce post.

L'objectif: démontrer les valeurs de passage de Controller1 à Controller2 et vice versa.

Le Flux De Programme:

  • La première scène contient un TextField , un Button , et un Label . Lorsque le Button est cliqué, la deuxième fenêtre est chargé et affiché, y compris le texte entré dans la TextField .
  • Dans la deuxième scène, il y a aussi un TextField , un Button , et un Label . Le Label affichera le texte entré dans le TextField sur la première scène.
  • en entrant le texte de la deuxième scène TextField et en cliquant sur son Button , la première scène Label est mise à jour pour montrer le texte entré.

démonstration très simple et qui pourrait certainement être améliorée, mais qui devrait rendre le concept très clair.

le code lui-même est également commenté avec quelques détails sur ce qui se passe et comment.

LE CODE

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>
3
répondu Zephyr 2018-06-26 20:20:45

vous devez créer une classe de contexte.

public class Context {
    private final static Context instance = new Context();
    public static Context getInstance() {
        return instance;
    }

    private Connection con;
    public void setConnection(Connection con)
    {
        this.con=con;
    }
    public Connection getConnection() {
        return con;
    }

    private TabRoughController tabRough;
    public void setTabRough(TabRoughController tabRough) {
        this.tabRough=tabRough;
    }

    public TabRoughController getTabRough() {
        return tabRough;
    }
}

vous devez juste définir l'instance du contrôleur dans l'initialisation en utilisant

Context.getInstance().setTabRough(this);

et vous pouvez l'utiliser à partir de l'ensemble de votre application en utilisant

TabRoughController cont=Context.getInstance().getTabRough();

Maintenant vous pouvez passer le paramètre à n'importe quel contrôleur de toute l'application.

1
répondu CTN 2016-10-22 14:14:44

Voici un exemple d'utilisation d'un contrôleur injecté par 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;
}

Voici une implémentation concrète du chargeur:

public class ConcreteViewLoader extends GuiceFxmlLoader {

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

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

notez que cet exemple charge la vue dans le centre d'un planeur qui est la racine de la scène dans la scène. Ce n'est pas pertinent pour l'exemple (détail de l'implémentation de mon cas d'utilisation spécifiques), mais a décidé de le laisser comme certains peuvent le trouver utile.

0
répondu jenglert 2015-11-20 00:48:45

vous pouvez décider d'utiliser une liste publique observable pour stocker des données publiques, ou tout simplement créer une méthode de setter public pour stocker des données et récupérer à partir du contrôleur correspondant

0
répondu Nospaniol Noah 2018-03-15 12:29:43