JavaFX Gleiche Controller-Instanz für inludiertes FXML

KrokoDiehl

Top Contributor
Hallo zusammen,

entweder ich stehe auf dem Schlauch oder es ist tatsächlich eine komplexere Sache in JavaFX:
Ich verwende JavaFX (8) mit FXML und möchte in einer - bereits großen - FXML-Datei eine andere inkludieren - hauptsächlich um die Datei nicht größer werden zu lassen.
Beide sollen aber die gleiche Controller-Instanz verwenden.

Hier ein Beispiel dass es verdeutlichen soll:
Java:
public class DerController {
  ...

  @FXML
  public void buttonImHauptFXMLgeklickt() {
  ... ich werde gefunden
  }

  @FXML
  public void buttonImSubFXMLgeklickt() {
  ... mich findet es nicht
  }
}

Die Haupt-FXML-Datei welche den Controller hat:
Code:
<VBox ...>
  <!-- viele UI-Komponenten -->
  <Button text="Haupt fxml" onAction="#buttonImHauptFXMLgeklickt" />

  <HBox>
  <fx:include source="subFxml.fxml" />
  </HBox>
</VBox>

Die Sub-FXML-Datei welche inkludiert wird:
Code:
<GridPane ....>
  <!-- diverse UI-Kompenten -->
  <Button text="Sub fxml" onAction="#buttonImSubFXMLgeklickt" />
</GridPane>

Geladen wird die Haupt-FXML-Komponenten über FXMLLoader wo ich die Controller-Instanz auch direkt setze. Das heißt der Controller wird nicht vom FXML instanziiert.
Für die inkludierte FXML-Datei möchte ich nun auch gerne die gleiche Controller-Instanz verwenden.

Starte ich das Ganze jedoch nach dem obigen Schema, bekomme ich folgenden Fehler:
Code:
Caused by: javafx.fxml.LoadException: No controller specified.
Die Stelle ist auch eindeutig: Dort wo im Sub-FXML die onAction-Deklaration ist hat er keinen Controller.

Kennt jemand eine Lösung oder hat sonstige Meinungen dazu? Ich kenne schon ein paar Workarounds, die arbeiten aber alle ohne ein fx:inlcude sondern laden das Sub-FXML separat und bauen die UI dann im Code zusammen.


Vielen Dank im Voraus!
 
Hi,
Ja, du kannst in ein include den selben Controller wie die "Hauptview" verwenden. Siehe nachfolgend ein Beispiel mit 3 FXMLs und 1.Controller.
ChildView1.fxml und ChildView2.fxml sind im MainView.fxml included. Alle 3 FXMLs benutzen den MainViewController.
MainView.fxml:
Code:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>

<AnchorPane prefHeight="280.0" prefWidth="500.0"
    xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2"
    fx:controller="test11.MainViewController">
    <children>
        <VBox prefHeight="280.0" prefWidth="500.0"
            AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
            AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
            <children>
                <fx:include fx:id="child1" source="ChildView1.fxml" />
                <fx:include fx:id="child2" source="ChildView2.fxml" />
            </children>
        </VBox>
    </children>
</AnchorPane>
MainViewController.java:
Java:
package test11;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public class MainViewController {
    @FXML
    private Parent view;
    @FXML
    private AnchorPane child1;
    @FXML
    private AnchorPane child2;

    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private Button button3;
    @FXML
    private Button button4;
    
    @FXML
    protected void handleButtonEvent(ActionEvent event) {
        String parentName = ((Node) event.getSource()).getParent().getParent().getId();
        System.out.println("ChildViewController: " + ((Button)event.getSource()).getText() + " von " + parentName + " wurde gedrückt...");
    }

    @FXML
    void initialize() {
        System.out.println("MainViewController.initialize()");
    }
}

ChildView1.fxml:
Code:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>

<AnchorPane fx:id="child1" id="child1" prefHeight="244.0" prefWidth="568.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="test11.MainViewController">
    <children>
        <HBox id="hBox1" layoutX="32.0" layoutY="14.0" prefHeight="51.0" prefWidth="200.0" spacing="10.0">
            <children>
                <Button fx:id="button1" prefHeight="37.0" text="Button 1" onAction="#handleButtonEvent" />
                <Button fx:id="button2" prefHeight="37.0" text="Button 2" onAction="#handleButtonEvent" />
            </children>
        </HBox>
    </children>
</AnchorPane>
ChildView2.fxml:
Code:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>

<AnchorPane fx:id="child2" id="child2" prefHeight="244.0" prefWidth="568.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="test11.MainViewController">
    <children>
        <HBox id="hBox2" layoutX="32.0" layoutY="14.0" prefHeight="51.0" prefWidth="200.0" spacing="10.0">
            <children>
                <Button fx:id="button3" prefHeight="37.0" text="Button 3" onAction="#handleButtonEvent" />
                <Button fx:id="button4" prefHeight="37.0" text="Button 4" onAction="#handleButtonEvent" />
            </children>
        </HBox>
    </children>
</AnchorPane>
Main.java:
Java:
package test11;

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

public class Main extends Application {

    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("MainView.fxml"));
            Parent rootElement = (Parent) loader.load();
            Scene scene = new Scene(rootElement, 800, 600);
            primaryStage.setTitle("JavaFX");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {    
        launch(args);
    }
}
Vielleicht wäre es aber besser, für jedes fxml einen separaten Controller zu erstellen, damit es übersichtlicher strukturiert werden kann... (ist nur so meine Meinung... ;-) )
 
Zuletzt bearbeitet:

Neue Themen


Zurück
Oben