Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Associating FXML and Controller in Guice's Module configuration

In my Guice Module I want to associate FXML files and their controllers, currently it looks like this:

public class GuiceModule extends AbstractModule 
{   
    @Override
    protected void configure()
    {
        // associate controllers and fxml files
        bind(MainController.class).toInstance((MainController)loadController("/main.fxml"));
        bind(SubController.class).toInstance((SubController)loadController("/content.fxml"));
    }

    protected Object loadController(String url)
    {
        InputStream fxmlStream = null;
        try
        {
            fxmlStream = getClass().getResourceAsStream(url);
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource(url));


            loader.setControllerFactory(new Callback<Class<?>, Object>() {

                public Object call(Class<?> clazz) { // clazz because class is a reserved word
                    return injector.getInstance(clazz); // PROBLEM: no access to the injector here
                }
            });

            loader.load(fxmlStream);
            return loader.getController();
        }

        // [..] exception handling

    }
}**strong text**

However in the loadController(String url) method I get problems with that line:

return injector.getInstance(clazz); // PROBLEM: no access to the injector here

How can I access Guice's getInstance method from within a Guice Module? Is that or something equivalent possible?

like image 463
stefan.at.wpf Avatar asked Apr 13 '13 17:04

stefan.at.wpf


People also ask

How do I specify a controller in FXML?

There are two ways to set a controller for an FXML file. The first way to set a controller is to specify it inside the FXML file. The second way is to set an instance of the controller class on the FXMLLoader instance used to load the FXML document.

Can two FXML have same controller?

It's possible to use the same controller class for multiple FXML files, but your code will be really hard to follow, and it's a very bad idea. (Also note that using the fx:controller attribute, you will have a different controller instance each time you call FXMLLoader.

Is FXML annotation required?

The @FXML annotation is required for the controller class's private member fields; otherwise, field injection would fail.

What is the full form of FXML?

FX Markup Language. In the same way that HTML is for Hypertext Markup Language, and many more acronyms that end in ML .


2 Answers

I am the author of fx-guice, an open-source library that can be used to use Guice in your JavaFX applications. The library is licensed using the Apache License v2 and can be obtained via the central Maven repository.

Even though it might not answer your exact question, I suggest you have a look at my project, which comes bundled with quite a few examples:
Project home: → http://github.com/cathive/fx-guice/

The main idea of my framework is: Instead of extending "javafx.application.Application" you extend "com.cathive.fx.GuiceApplication". You can then simply @Inject instaces of "GuiceFXMLLoader" wherever you want and can use these special FXMLLoader instances to load your UI definitions. Within your FXML-controller classes you can mix @Inject and @FXML annoations as you like.
→ http://bit.ly/139fKQV

My framework also offers a bunch of functionality concerning "Guicified" JavaFX components, which bind together a Java class and a single FXML file (using a special annotation: "@FXMLComponent"). I wrote a short "Calculator" example whose sources can be obtained from the Github pages (see above). The relevant portions of the code can be found here:
CalculatorAppPane.java: → http://bit.ly/10YMVoM
CalculatorAppPane.fxml: → http://bit.ly/13loYv8

Hope that helps. :-)

like image 140
headcr4sh Avatar answered Oct 05 '22 23:10

headcr4sh


I will suggest one of many approaches to associate a Controller to an FXML file, I will suppose that you are using the fx:controller tag in your FXML file.

for the demonstration purpose, I will implement a demo app hosted on github with one button in the middle of the stage.

enter image description here

Demo__
     |___ App.java
     |___ AppModule.java
     |___ IController.java
     |___ Controller.java
     |___ InjectingFXMLLoader.java
     |___ fxml
              |__view.fxml

View.fxml

note that we refer to the interface in the fx:controller in the FXML file, and not to the implementation, so we can reuse the fxml view with other controllers implementing the interface.

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane fx:id="root" xmlns:fx="http://javafx.com/fxml" fx:controller="org.tarrsalah.stackoverflow.guice.fx.IController" >
    <center>
        <HBox alignment="CENTER" prefHeight="-1.0" prefWidth="-1.0">
            <children>
                <Button fx:id="fx_print" alignment="CENTER" contentDisplay="CENTER" defaultButton="true" mnemonicParsing="false" onAction="#printButton" text="Print !" HBox.hgrow="ALWAYS" />
            </children>
            <BorderPane.margin>
                <Insets bottom="20.0" top="10.0" />
            </BorderPane.margin>
        </HBox>
    </center>
</BorderPane>

IController

an interface that the controller must implement , the printButton() to print a message to the screen , and getRoot() to get the Panel View.

import javafx.fxml.Initializable;
import javafx.scene.Parent;

public interface IController extends Initializable {

    public void printButton();  

    public Parent getRoot();
}

Controller

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.scene.Parent;

public class Controller implements IController {

    @FXML
    Parent root;

    public void initialize(URL url, ResourceBundle rb) {
    }

    public Parent getRoot() {
        return root;
    }

    public void printButton() {
        System.out.println("Hello Guice !!");
    }
}

InjectingFXMLLoader

a class with one static method that get a concrete implementation of a controller and URL of the FXML file and return the controller of the view.

import java.io.IOException;
import java.net.URL;
import javafx.fxml.FXMLLoader;
import javafx.util.Callback;

public class InjectingFXMLLoader {
    /**
     * 
     * @param <N>
     * @param injector
     * @return a controller injected within an FXML.
     */
    public static <N> N loadFXMLController(final N controller, URL url) throws IOException {
        FXMLLoader loader= new FXMLLoader();
        loader.setLocation(url);
        loader.setControllerFactory(new Callback<Class<?>, Object>() {

            public Object call(Class<?> p) {
                return controller;
            }
        });
        loader.load();
        return loader.getController();
    }
}

AppModule

in the guice module , we use the InjectingFXMLLoader class to associate a concrete implementation of the controller with the corresponding FXML file. using a @Provides method.

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import java.io.IOException;

public class AppModule extends AbstractModule {

    @Override
    protected void configure() {
    }

    /**
     *
     * @return IController
     * @throws IOException
     */
    @Provides
    public IController getIController() throws IOException {
        return InjectingFXMLLoader.loadFXMLController(new Controller(), getClass().getClassLoader().getResource("fxml/view.fxml"));
    }
}

App

the main class that show the view

import com.google.inject.Guice;
import javafx.application.Application;
import javafx.scene.SceneBuilder;
import javafx.stage.Stage;

public class App extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(SceneBuilder
                .create()
                .root(Guice.createInjector(new AppModule()).getInstance(IController.class).getRoot())
                .height(160)
                .width(200)
                .build());
        stage.show();
    }
}
like image 20
Salah Eddine Taouririt Avatar answered Oct 06 '22 01:10

Salah Eddine Taouririt