I read few spring tutorials with basic examples, and I am a bit confused on how to wire up things properly.
The trouble is, I wanted to use application context to pull singleton controller references, but I read on few other topics that application context should not be accessed directly unless it is absolutely necessary. I think I should use constructor to instantiate reference I want, but here things get all blurry for me.
I have javafx application with several fxml files. I have one main fxml and other are loaded dynamically inside main.
I'll use simplified code for example with two fxml controllers, MainController.java (for main fxml) and ContentController.java (for content fxml)
The idea is that content fxml has TabPane, and main fxml has button which opens new tab in TabPane on ContentController.
I am currently doing something like this
bean xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="contentController"
class="ContentController"
scope="singleton" />
</beans>
MainControler:
public class MainOverlayControler {
ApplicationContext context;
#FXML
private BorderPane borderPane;
#FXML
private void initialize() {
loadContentHolder();
}
#FXML
private Button btn;
#FXML
private void btnOnAction(ActionEvent evt) {
((ContentController)context.getBean("contentController")).openNewContent();
}
private void loadContentHolder() {
//set app context
context = new ClassPathXmlApplicationContext("Beans.xml");
Node fxmlNode;
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setController(context.getBean("contentController"));
try {
fxmlNode = (Node)fxmlLoader.load(getClass().getResource("Content.fxml").openStream());
borderPane.setCenter(fxmlNode);
} catch (IOException e) {
e.printStackTrace();
}
}
ContentController:
public class ContentController {
#FXML
private TabPane tabPane;
public void openNewContent() {
Tab newContentTab = new Tab();
newContentTab.setText("NewTab");
tabPane.getTabs().add(newContentTab);
}
}
Main class:
public class MainFX extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = (Parent) fxmlLoader.load(getClass().getResource("main.fxml").openStream());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
public static void main(String[] args) {
launch(args);
}
}
Question:
I would like to know how to do same thing with constructor DI, or some other way if I misunderstood the concept.
I need to be able to call "openNewContent" on singleton instance of "ContentController" from multiple other controllers as well.
Read some more on JavaFX and Spring integration. I recommend series by Stephen Chin
Basically, You need to embed your JavaFX classes into Spring context life cycle, and it is very nicely described in linked article.
You can also check his example project on GitHub, where the code is simplified with controller factories introduced in JavaFX 2.1 (check here)
Related
I have an app, which has HomeScene.fxml file with headers and menu. HomeScene has also dashboardPane, which should be changed dynamically after menu button is being pressed. Dashboard pane content should be loaded from another fxml file, lets say 'FinancesPane.fxml' or 'SettingsPane.fxml'.
Im trying to replace content of dashboardPane in HomeController:
#FXML
public void handleFinancesButtonAction() {
FinancesPaneFactory paneFactory = new FinancesPaneFactory();
dashBoardPane.getChildren().clear();
dashBoardPane.getChildren().add(paneFactory.createPane());
}
My FinancesPaneFactory looks like this:
public class FinancesPaneFactory extends PaneFactory {
private static final String PANE_TEMPLATE_PATH = "/sceneTemplates/FinancesPane.fxml";
public FinancesPaneFactory() {
super(PANE_TEMPLATE_PATH );
}
#Override
protected Pane generatePane(FXMLLoader loader) {
try {
return (Pane) loader.load();
} catch (IOException e) {
throw new FatBirdRuntimeException("Unable to load FinancesPane", e);
}
}
}
To be more clear, this is how HomeScene looks like: HomeScene .
This empty space is a dashboardPane, and should be replaced with another content when user press the left menu button.
How to inject this content dynamically?
Yes, you should do this to keep scene graph low and you will benefit from better performance , what i do is create dynamic container :
#FXML
private ScrollPane dynamicNode;
Scroll pane is a good choice.
This is put to MainController.
I have main controller different from others , main controller is actually the only one i initialize, so in your main program class whatever you call it :
private static MainViewController mainViewController;
...
private static BorderPane loadMainPane() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setController(mainViewController);
BorderPane mainPane = (BorderPane) loader.load(
CsgoRr.class
.getResourceAsStream(Info.Resource.FXML_FILE_MAIN));
mainPane.getStylesheets().add(CsgoRr.class.getResource("path...style.css").toString());
return mainPane;
}
Dont forget to create static accessor, other controllers that i have are usually not created this way , i use fx:controller in fxml to specify what controller should be for which fxml , its usually handy to have mainController accessable.
So to change your views create in your main controller methods that are connected to your menu with whose you change views
#FXML
private void setViewPreferences() {
setView(Info.Resource.FXML_FILE_PREFERENCES);
}
#FXML
private void setViewProductPage() {
setView(Info.Resource.FXML_FILE_PRODUCT_PAGE);
}
Currently in dynamicNode is helper to see what exactly is the current selected, its
private String currentlyInDynamicPane;//not important
Here is setView
public void setView(String fxmlPath) {
dynamicNode.setContent(getView(fxmlPath));
currentlyInDynamicPane = fxmlPath;
}
public Node getView(String fxmlPath) {
try {
return new FXMLLoader(getClass().getResource(fxmlPath)).load();
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
So when you click left menu you swap FXML files, you can make sure that you have some default FXML shown at the start or when nothing in menu is selected as well.
This is the way i do it, roughly.
So think about YOUR DASHBOARD as DynamicPane,
I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.
Is this possible? I havent found any way to do it.
Static functions could be a way, but they don't have access to the form's controls.
Any ideas?
You can get the controller from the FXMLLoader
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();
store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:
getFooController().updatePage(strData);
updatePage() can be something like:
// ...
#FXML private Label lblData;
// ...
public void updatePage(String data){
lblData.setText(data);
}
// ...
in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.
Also look the https://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.
Just to help clarify the accepted answer and maybe save a bit of time for others that are new to JavaFX:
For a JavaFX FXML Application, NetBeans will auto-generate your start method in the main class as follows:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now, all we need to do to have access to the controller class is to change the FXMLLoader load() method from the static implementation to an instantiated implementation and then we can use the instance's method to get the controller, like this:
//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;
#Override
public void start(Stage stage) throws Exception {
//Set up instance instead of using static load() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
//Now we have access to getController() through the instance... don't forget the type cast
myControllerHandle = (MyController)loader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Another solution is to set the controller from your controller class, like so...
public class Controller implements javafx.fxml.Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
// Implementing the Initializable interface means that this method
// will be called when the controller instance is created
App.setController(this);
}
}
This is the solution I prefer to use since the code is somewhat messy to create a fully functional FXMLLoader instance which properly handles local resources etc
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}
versus
#Override
public void start(Stage stage) throws Exception {
URL location = getClass().getResource("/sample.fxml");
FXMLLoader loader = createFXMLLoader(location);
Parent root = loader.load(location.openStream());
}
public FXMLLoader createFXMLLoader(URL location) {
return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}
On the object's loading from the Main screen, one way to pass data that I have found and works is to use lookup and then set the data inside an invisible label that I can retrieve later from the controller class. Like this:
Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData);
This works, but there must be a better way.
I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.
Is this possible? I havent found any way to do it.
Static functions could be a way, but they don't have access to the form's controls.
Any ideas?
You can get the controller from the FXMLLoader
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();
store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:
getFooController().updatePage(strData);
updatePage() can be something like:
// ...
#FXML private Label lblData;
// ...
public void updatePage(String data){
lblData.setText(data);
}
// ...
in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.
Also look the https://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.
Just to help clarify the accepted answer and maybe save a bit of time for others that are new to JavaFX:
For a JavaFX FXML Application, NetBeans will auto-generate your start method in the main class as follows:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now, all we need to do to have access to the controller class is to change the FXMLLoader load() method from the static implementation to an instantiated implementation and then we can use the instance's method to get the controller, like this:
//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;
#Override
public void start(Stage stage) throws Exception {
//Set up instance instead of using static load() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
//Now we have access to getController() through the instance... don't forget the type cast
myControllerHandle = (MyController)loader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Another solution is to set the controller from your controller class, like so...
public class Controller implements javafx.fxml.Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
// Implementing the Initializable interface means that this method
// will be called when the controller instance is created
App.setController(this);
}
}
This is the solution I prefer to use since the code is somewhat messy to create a fully functional FXMLLoader instance which properly handles local resources etc
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}
versus
#Override
public void start(Stage stage) throws Exception {
URL location = getClass().getResource("/sample.fxml");
FXMLLoader loader = createFXMLLoader(location);
Parent root = loader.load(location.openStream());
}
public FXMLLoader createFXMLLoader(URL location) {
return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}
On the object's loading from the Main screen, one way to pass data that I have found and works is to use lookup and then set the data inside an invisible label that I can retrieve later from the controller class. Like this:
Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData);
This works, but there must be a better way.
JavaFX itself has some means of DI to allow binding between XML-described UIs and controllers:
<Pane fx:controller="foo.bar.MyController">
<children>
<Label fx:id="myLabel" furtherAttribute="..." />
</children>
</Pane>
The Java-side looks like this:
public class MyController implements Initializable {
#FXML private Label myLabel;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
// FXML-fields have been injected at this point of time:
myLabel.setText("Hello world!");
}
}
For this to work, I can not just create an instance of MyController. Instead I have to ask JavaFX to do stuff for me:
FXMLLoader loader = new FXMLLoader(MyApp.class.getResource("/fxml/myFxmlFile.fxml"), rb);
loader.load();
MyController ctrl = (MyController) loader.getController();
So far, so good
However, if I want to use Dagger 2 to inject some non-FXML-dependencies into the constructor of this controller class, I have a problem, as I have no control over the instantiation process, if I use JavaFX.
public class MyController implements Initializable {
#FXML private Label myLabel;
/*
How do I make this work?
private final SomeService myService;
#Inject
public MyController(SomeService myService) {
this.myService = myService;
}
*/
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
// FXML-fields have been injected at this point of time:
myLabel.setText("Hello world!");
}
}
There is one API that looks promising: loader.setControllerFactory(...); Maybe this is a good point to start with. But I do not have enough experience with these libraries to know how to approach this problem.
A custom ControllerFactory would need to construct Controllers of certain types only known at runtime. This could look like the following:
T t = clazz.newInstance();
injector.inject(t);
return t;
This is perfectly ok for most other DI libraries like Guice, as they just have to look up dependencies for the type of t in their dependency graph.
Dagger 2 resolves dependencies during compile time. Its biggest features is at the same time its biggest problem: If a type is only known at runtime the compiler can not distinguish invocations of inject(t). It could be inject(Foo foo) or inject(Bar bar).
(Also this wouldn't work with final fields, as newInstance() invokes the default-constructor).
Ok no generic types. Lets look at a second approach: Get the controller instance from Dagger first and pass it to the FXMLLoader afterwards.
I used the CoffeeShop example from Dagger and modified it to construct JavaFX controllers:
#Singleton
#Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
Provider<CoffeeMakerController> coffeeMakerController();
}
If I get a CoffeeMakerController, all its fields are already injected, so I can easily use it in setController(...):
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
CoffeeMakerController ctrl = coffeeShop.coffeeMakerController().get();
/* ... */
FXMLLoader loader = new FXMLLoader(fxmlUrl, rb);
loader.setController(ctrl);
Parent root = loader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
My FXML file must not contain a fx:controller attribute, as the loader would try to construct a controller, which of course stands in conflict with our Dagger-provided one.
The full example is available on GitHub
Thanks to Map multibinding mechanism hint from #Sebastian_S I've managed to make automatic controller binding using Map<Class<?>, Provider<Object>> that maps each controller to its class.
In Module collect all controllers into Map named "Controllers" with corresponding Class keys
#Module
public class MyModule {
// ********************** CONTROLLERS **********************
#Provides
#IntoMap
#Named("Controllers")
#ClassKey(FirstController.class)
static Object provideFirstController(DepA depA, DepB depB) {
return new FirstController(depA, depB);
}
#Provides
#IntoMap
#Named("Controllers")
#ClassKey(SecondController.class)
static Object provideSecondController(DepA depA, DepC depC) {
return new SecondController(depA, depC);
}
}
Then in Component, we can get an instance of this Map using its name. The value type of this map should be Provider<Object> because we want to get a new instance of a controller each time FXMLLoader needs it.
#Singleton
#Component(modules = MyModule.class)
public interface MyDiContainer {
// ********************** CONTROLLERS **********************
#Named("Controllers")
Map<Class<?>, Provider<Object>> getControllers();
}
And finally, in your FXML loading code, you should set new ControllerFactory
MyDiContainer myDiContainer = DaggerMyDiContainer.create()
Map<Class<?>, Provider<Object>> controllers = myDiContainer.getControllers();
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(type -> controllers.get(type).get());
Alternatively you can do something like:
...
loader.setControllerFactory(new Callback<Class<?>, Object>() {
#Override
public Object call(Class<?> type) {
switch (type.getSimpleName()) {
case "LoginController":
return loginController;
case "MainController":
return mainController;
default:
return null;
}
}
});
...
As #Sebastian_S noted, a reflection-based controller factory is not possible. However calling setController is not the only way, I actually like this setControllerFactory approach better because it doesn't break the tooling (e.g. IntelliJ's XML inspections) but having to explicitly list out all the classes is definitely a drawback.
This is solved probably long ago for many people. I did not like the solution described here in as it relies on class names or reflection over clean design. I wrote a bit different one that looks more maintainable to my eyes.
The gist of it is to use Dagger to create the Scene that is injected into the Stage. Here is my Application class
CameraRemote context;
public static void main(String[] args) {
launch(args);
}
public SimpleUI() {
context = DaggerCameraRemote.builder().build();
}
#Override
public void start(Stage stage) throws IOException {
stage.setTitle("Remote Control");
stage.setScene(context.mainFrame());
stage.show();
}
I have in my Dagger 2 module the logic for loading fxml and customization of the controller i.e. injecting the SsdpClient
#Provides
public static Scene provideMainScene(SsdpClient ssdpClient) {
try {
FXMLLoader loader = new FXMLLoader(CameraModule.class.getResource("/MainFrame.fxml"));
Parent root;
root = loader.load();
MainController controller = (MainController) loader.getController();
controller.setClient(ssdpClient);
return new Scene(root, 800, 450);
} catch (IOException e) {
throw new RuntimeException("Cannot load MainFrame.fxml", e);
}
}
I can split further the creation of Parent instance. It is not used anywhere else and I compromised.
I've recently delved into JavaFX as a C# developer. One thing I noticed in Java is that you're not spoon fed the way Visual Studio/Microsoft spoonfeed us.
So. When I was creating a form using the scene builder for IntelliJ Idea on JavaFX. I inherited "Stage" for my controller class and created a void called load that will load the instance of the scene from the FXML file. Therefore when I call load() from the Main entry point or anywhere it will load the fxml file and show.
LoginController frmLogin = new LoginController();
frmLogin.load();
The problem is that it works and it does't work.
Here's my code.
Main.Java
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
LoginController frmLogin = new LoginController();
frmLogin.load();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
LoginController.Java
public class LoginController extends Stage
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
public void load() throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
this.setScene(new Scene(root));
this.setTitle("JavaFX GUI");
this.setResizable(false);
this.initModality(Modality.APPLICATION_MODAL);
this.show();
}
}
Here's a GIF of the problem.
http://i.imgur.com/0hOG76M.gif
I want to know why when I call .show() it shows a blank for?
Any help would be appreicated.
Solution
Don't inherit Stage in your Controller.
JavaFX will implicitly create a Stage for your application and pass it to your application (the primaryStage parameter in your application start method).
Sample
Here is a quick update which should work. Another alternative for this is to factor out the stage management as in James's answer.
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
. . .
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Aside: I am not sure what your MessageBox class is, but JavaFX 8u40 has a built-in Alert dialog box for standard message box style functionality, so that would be the preferred method to do that.
It looks like you have confused the different pieces that make up the application.
The FXML typically represents the "view"; i.e. the portion of the UI that is visible. It defines what controls are displayed and how they are laid out.
The controller implements the logic that is connected to (controls) the view. So it typically processes user input and updates the view in various ways.
A Stage is a window.
So, I don't think it really makes sense that your controller is a Stage. There are some scenarios where you might make a controller a subclass of a UI element, but those are somewhat advanced uses of JavaFX, and even then you would typically subclass a layout pane, not a Stage.
Here's roughly what happens when you call load on an FXMLLoader:
The FXMLLoader creates a hierarchy of Nodes (UI elements) corresponding to the elements defined in the FXML file
If the FXML file defines a fx:controller attribute in its root element, the FXMLLoader constructs a new instance of that class. It then injects any elements with fx:id attributes into fields in that controller instance with names matching the fx:id values. It also registers any event handlers mapping to methods in the controller instance.
The FXMLLoader's load() method returns the object corresponding to the root element of the FXML file.
So, in your code, you actually end up with two LoginController instances. You create one yourself in the start() method. You then call load() on that instance. That method calls load(...) on an FXMLLoader (via the really ugly static load method). Calling FXMLLoader.load(...) then causes the FXMLLoader to create an instance of the class declared in fx:controller. I'm guessing (you didn't show the FXML code) that class is also LoginController. So that is the second instance.
Now what happens, is that you get a reference to the UI element from FXMLLoader.load(). You put that in a Scene, and set the Scene in the LoginController, which - unusually - is a Stage. Then you make the Stage appear on the screen with show(). Note this happens in the instance you created in the start method.
When the user presses the button that has btnLogin_Clicked registered as its handler, the handler method is invoked on the controller instance: the one created by the FXMLLoader. That instance never had a Scene set, so when you then call this.show() it shows that instance of the LoginController (which, again, is a Stage). Since it never had its scene set, you see a blank window.
It's not actually clear to me what you intend with the call to this.show() in btnLogin_Clicked anyway. Assuming you thought this was the same Stage you had created from the start(...) method, that Stage is already showing.
The typical pattern is that you use the primaryStage that is passed to the start(...) method, and set a scene in that and show it. So you'd do something like:
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.initModality(Modality.APPLICATION_MODAL);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
and then the controller is just a controller: it just handles the logic:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
// I don't really know what you were trying to do here
// but if you need a reference to the window containing the
// associated fxml elements, you can get it from one of those
// elements:
Stage stage = (Stage) txtUsername.getScene().getWindow();
//this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Typically what you want to do when the user has successfully logged in, is to display something new in the current window. The simplest way to do this is just to set the root of the current scene to the content of a different FXML file. For example:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
Scene currentScene = txtUsername.getScene();
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
currentScene.setRoot(root);
// resize window:
currentScene.getWindow().sizeToScene();
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Here Main.fxml defines the main application the user sees, having successfully logged in, and defines its own controller class, etc.