How to pass a value to fxml scene - user-interface

I'm making this javaFX application, but in a given moment i wanna get the text exibited in the scene from a method in anothe class, how can i pass this value to my text box in the FXML doccument?
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.72" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.crapgames.calourosimulator.views.pcna.intro.Introduction">
<children>
<ImageView fx:id="imgClick" fitHeight="600.0" fitWidth="800.0" onMouseClicked="#nextScene" pickOnBounds="true">
<image>
<Image url="#../../assets/pnca-monitor-text.png" />
</image>
</ImageView>
<Text fx:id="text1" fill="#f5eded" layoutX="8.0" layoutY="452.0" strokeType="OUTSIDE" strokeWidth="0.0" text="where i want my string variable" wrappingWidth="787.0">
<font>
<Font name="Comic Sans MS" size="25.0" />
</font></Text>
</children>
</AnchorPane>

Create a method in the controller class to accept the text value. You can leverage the textProperty of the Text if you want:
public class Introduction {
#FXML
private Text text1 ;
public StringProperty textProperty() {
return text1.textProperty();
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
// existing code ...
}
Now you can do:
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
Parent root = loader.load();
Introduction controller = loader.getController();
controller.setText("Hello World!");
Scene scene = new Scene(root);
// etc...

Related

How to load an fxml file from a controller that is linked to a different fxml file?

I am building a JavaFX which has two windows: a signin window and a register window. The fxml file of the former has a "username" label and text field, a "password" label and text field, a "signin" button, a "close" button and a hyperlink that reads "or create a new user". I want that when users click on the hyperlink, a registration window pop-up with labels and text fields asking for the user to enter a user name, password, first name, etc.
I have a Login.fxml file and a LoginController.java file for the sign in window. And for the registration window I have a CreateUser.fxml file and a CreateUSerController.java file.
My problem is how to load the CreateUser.fxml file (and make the registration window pop-up) from the LoginController.java file. I handle the clicking on the "or create a new user" hyperlink event in LoginController.java, and basically the result I expect of the handling is to make the registration window to pop-up. I get a runtime error.
When I click on the "or create a new user" hyperlink in the login window I get the following error: The Eclipse debugger says "Thread[JavaFX Application Thread](Suspended(uncaught exception RuntimeException))". Also, when I check the value of the variables during execution there is a "runWithoutRenderLock() is throwing", and its value is RuntimeException. Also says that its cause is a "InvocationTargetException", and that the cause of this exception is a "null". Also, a "QuantumToolkit.class" tab appears in eclipse with the message "Source not found" "The JAR file ... has no source attachment". It also has a clickable button that reads "Attach source"
To load "CreateUser.fxml" from "LoginController.java" I used a handler which tries to load the fxml file with this line of code: "root = FXMLLoader.<Parent>load(CreateUserController.class.getResource("CreateUser.fxml"));". Here is where the exception occurs
Please find below my Main class which is where I load the "Login.fxml" file, the "Login.fxml", "LoginController.java", "CreateUser.fxml" and CreateUSerController.java files. Also, an image of my file structure.
File structure
src
/application
-Main.java
/controller
-LoginController.java
-CreateUserController.java
/model
/view
-Login.fxml
-CreateUser.fxml
-module-info.java
Main.java
package application;
import java.io.IOException;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
#Override
public void start(Stage signinStage) throws IOException {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("/view/Login.fxml"));
Scene scene = new Scene(root,400,400);
signinStage.setTitle("Welcome to MyHEalth");
signinStage.setScene(scene);
signinStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
launch(args);
}
}
Login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.LoginController">
<center>
<VBox alignment="CENTER" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="400.0" BorderPane.alignment="CENTER">
<children>
<Label maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="21.0" prefWidth="300.0" text="Username">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<TextField fx:id="userNameField" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="25.0" prefWidth="300.0">
<VBox.margin>
<Insets bottom="20.0" />
</VBox.margin>
</TextField>
<Label prefHeight="17.0" prefWidth="300.0" text="Password">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<TextField fx:id="passwordField" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="25.0" prefWidth="300.0" />
<BorderPane prefHeight="100.0" prefWidth="200.0">
<center>
<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="30.0" prefWidth="170.0" BorderPane.alignment="CENTER">
<children>
<Button fx:id="signinButton" maxWidth="-Infinity" mnemonicParsing="false" onAction="#signin" prefWidth="70.0" text="Sign in" />
<Button fx:id="closeButton" alignment="CENTER" maxWidth="-Infinity" mnemonicParsing="false" onAction="#close" prefWidth="60.0" text="Close" textAlignment="RIGHT">
<HBox.margin>
<Insets left="40.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</center>
<bottom>
<Hyperlink fx:id="newUserLink" onAction="#openNewUserWindow" text="or create new user" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>
</children>
</VBox>
</center>
</BorderPane>
LoginController.java
package controller;
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import model.User;
public class LoginController {
#FXML
private Button closeButton;
#FXML
private Hyperlink newUserLink;
#FXML
private TextField passwordField;
#FXML
private Button signinButton;
#FXML
private TextField userNameField;
private Stage stage;
private Parent root;
#FXML
void signin(ActionEvent event) {
passwordField.getText();
userNameField.getText();
User user = new User(passwordField.getText(), "first name", "last name", userNameField.getText(), "imagepath");
//still have to get the rest of the information from the database
System.out.print(user.getProfile().getFirstName());//debugging message
}
#FXML
void close(ActionEvent event) {
System.out.print("Thanks for visiting MyHealth. Bye");
System.exit(0);
}
#FXML
void openNewUserWindow(ActionEvent event) {
try {
System.out.print("1st line Inside 'try' block of 'openNewUserWindow' in LoginController");
//System.out.println("\nJust after FXMLLoader Inside 'try' block of 'openNewUserWindow' in LoginController");
root = FXMLLoader.<Parent>load(CreateUserController.class.getResource("CreateUser.fxml"));
//System.out.print("last line Inside 'try' block of 'openNewUserWindow' in LoginController");
}
catch(IOException e){
System.out.print("Problem getting 'CreateUSer.fxml'");
}
Stage createUserStage = new Stage();
Scene createUserScene = new Scene(root, 400, 400);
createUserStage.setScene(createUserScene);
createUserStage.setTitle("Create a new user");
createUserStage.show();
}
}
CreateUser.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.CreateUserController">
<children>
<VBox layoutY="30.0" prefHeight="440.0" prefWidth="400.0">
<children>
<ImageView fitHeight="82.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true">
<VBox.margin>
<Insets left="150.0" top="10.0" />
</VBox.margin>
</ImageView>
<Label fx:id="clickImage" text="Click to select profile picture" textFill="#00000093">
<VBox.margin>
<Insets bottom="20.0" left="125.0" />
</VBox.margin>
</Label>
<Label text="Username">
<VBox.margin>
<Insets bottom="5.0" left="50.0" />
</VBox.margin>
</Label>
<TextField fx:id="userName" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="25.0" prefWidth="280.0">
<VBox.margin>
<Insets bottom="10.0" left="50.0" />
</VBox.margin>
</TextField>
<Label text="First name">
<VBox.margin>
<Insets bottom="5.0" left="50.0" />
</VBox.margin>
</Label>
<TextField fx:id="firstName" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="280.0">
<VBox.margin>
<Insets bottom="10.0" left="50.0" />
</VBox.margin>
</TextField>
<Label text="Last name">
<VBox.margin>
<Insets bottom="5.0" left="50.0" />
</VBox.margin>
</Label>
<TextField fx:id="lastName" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="280.0">
<VBox.margin>
<Insets bottom="10.0" left="50.0" />
</VBox.margin>
</TextField>
<Label text="Password">
<VBox.margin>
<Insets bottom="5.0" left="50.0" />
</VBox.margin>
</Label>
<TextField fx:id="password" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="280.0">
<VBox.margin>
<Insets bottom="10.0" left="50.0" />
</VBox.margin>
</TextField>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="createButton" mnemonicParsing="false" onAction="#create" text="Create user">
<HBox.margin>
<Insets left="100.0" top="15.0" />
</HBox.margin>
</Button>
<Button fx:id="closeButton" mnemonicParsing="false" onAction="#close" text="Close">
<HBox.margin>
<Insets left="30.0" top="15.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<Label fx:id="createdLabel" text="Created user" textFill="#2b784f">
<VBox.margin>
<Insets left="125.0" top="20.0" />
</VBox.margin>
</Label>
</children>
</VBox>
</children>
</VBox>
</children>
</AnchorPane>
CreateUSerController.java
package controller;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class CreateUserController {
#FXML
private Label clickImage;
#FXML
private Button closeButton;
#FXML
private Button createButton;
#FXML
private Label createdLabel;
#FXML
private TextField firstName;
#FXML
private TextField lastName;
#FXML
private TextField password;
#FXML
private TextField userName;
#FXML
void close(ActionEvent event) {
}
#FXML
void create(ActionEvent event) {
}
}
First of all, I personally prefer to instantiate a new FXMLLoader instance to load a fxml file. Then you can also specify a location and some other kind of stuff like a controller factory.
You can lookup the setter methods here: https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html
But in this case your only mistake is that you load the fxml from the wrong class location.
When you use "CreateUserController.class.getResource("CreateUser.fxml")", it will try to find the fxml file in the same directory like the class. You have to use another path like you did before with the login fxml file. ("/view/CreateUser.fxml")
I hope that helps.

javafx 8 - table pagination issue stack overflow

This is the layout I want to make
This is fxml file: category.main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Pagination?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane prefHeight="381.0" prefWidth="899.0" styleClass="background-white" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ciro.hm.controller.CategoryMainController">
<children>
<VBox prefHeight="400.0" prefWidth="573.0" style="-fx-min-width: 80%;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="321.0" AnchorPane.topAnchor="10.0">
<children>
<BorderPane>
<center>
<TableView fx:id="table" prefHeight="323.0" prefWidth="573.0">
<columns>
<TableColumn fx:id="colId" editable="false" minWidth="30.0" prefWidth="75.0" resizable="false" styleClass="table-cell" text="%tbl.id" />
<TableColumn fx:id="colName" editable="false" prefWidth="253.0" resizable="false" styleClass="table-cell" text="%tbl.name" />
<TableColumn fx:id="colInOut" editable="false" minWidth="0.0" prefWidth="155.0" styleClass="table-cell" text="%tbl.in.out" />
<TableColumn fx:id="colBtn" editable="false" minWidth="0.0" prefWidth="90.0" resizable="false" sortable="false" styleClass="table-cell" text="#" />
</columns>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</TableView>
</center>
</BorderPane>
<Pagination fx:id="pagination" prefHeight="51.0" prefWidth="573.0" />
</children>
</VBox>
<VBox layoutX="448.0" maxWidth="203.0" minWidth="-Infinity" prefHeight="400.0" prefWidth="300.0" AnchorPane.rightAnchor="7.0" AnchorPane.topAnchor="10.0">
<children>
<TitledPane animated="false" collapsible="false" prefHeight="264.0" prefWidth="400.0" text="%app.pane.category">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="230.0" prefWidth="201.0">
<children>
<Button fx:id="btnSave" layoutX="17.0" layoutY="151.0" mnemonicParsing="false" text="%app.lbl.save" AnchorPane.leftAnchor="0.0" />
<Label layoutX="14.0" layoutY="14.0" text="%app.lbl.name" AnchorPane.leftAnchor="0.0" />
<ComboBox fx:id="fldInOut" layoutX="17.0" layoutY="101.0" prefWidth="150.0" promptText="%app.generic.selection" AnchorPane.leftAnchor="0.0" />
<Label layoutX="71.0" layoutY="80.0" text="%app.lbl.in.out" AnchorPane.leftAnchor="0.0" />
<TextField fx:id="fldName" layoutX="14.0" layoutY="35.0" AnchorPane.leftAnchor="0.0" />
</children>
</AnchorPane>
</content>
</TitledPane>
</children>
</VBox>
</children>
</AnchorPane>
This is controller
package com.ciro.hm.controller;
import com.ciro.hm.model.Category;
import com.ciro.hm.service.CategoryService;
import com.ciro.hm.to.CategoryTO;
import de.jensd.fx.glyphs.GlyphsDude;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
#Component
#Slf4j
public class CategoryMainController extends BaseController implements Initializable {
ObservableList<Category> data;
#FXML
BorderPane mainBorder;
#Value("${app.hm.in-out-value}")
List<String> inOut;
#FXML
TableView<Category> table;
#FXML
TextField fldName;
#FXML
ComboBox<String> fldInOut;
#FXML
TableColumn<Category, Integer> colId;
#FXML
TableColumn<Category, String> colName;
#FXML
TableColumn<Category, String> colInOut;
#FXML
TableColumn<Category, String> colBtn;
private CategoryService categoryService;
#Autowired
public CategoryMainController(CategoryService categoryService){
this.categoryService = categoryService;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
super.initialize(location, resources);
data = categoryService.findAll();
fldInOut.getItems().removeAll();
fldInOut.getItems().addAll(inOut);
btnSave.setOnAction(this::save);
pagination.setCurrentPageIndex(0);
pagination.setPageCount(getPageSize(data));
pagination.setPageFactory(this::createPage);
table.setItems(FXCollections.observableArrayList(data.subList(0, paginationSize)));
colId.setCellValueFactory(new PropertyValueFactory<>("idCategory"));
colName.setCellValueFactory(new PropertyValueFactory<>("name"));
colInOut.setCellValueFactory(new PropertyValueFactory<>("inOut"));
colBtn.setCellFactory(getEditCellFactory());
}
private Callback<TableColumn<Category, String>, TableCell<Category, String>> getEditCellFactory() {
return new Callback<TableColumn<Category, String>, TableCell<Category, String>>() {
#Override
public TableCell<Category,String> call(final TableColumn<Category, String> param) {
return new TableCell<Category, String>() {
Button btn = getEditBtn();
{
btn.setOnAction((ActionEvent event) -> {
Category data = getTableView().getItems().get(getIndex());
save(event);
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
setGraphic(btn);
setText(null);
}
}
};
}
};
}
#Override
public Button getEditBtn(){
Button b = GlyphsDude.createIconButton(FontAwesomeIcon.EDIT,btnEditText,iconSize,fontSize, ContentDisplay.GRAPHIC_ONLY);
btnEditCssClass.forEach(s->b.getStyleClass().add(s));
return b;
}
private void save(ActionEvent event) {
log.debug(event.toString());
CategoryTO categoryTO = new CategoryTO(fldName.getText(), (String) fldInOut.getValue());
categoryService.save(categoryTO);
}
private Node createPage(int pageIndex) {
int fromIndex = pageIndex * paginationSize;
int toIndex = Math.min(fromIndex + paginationSize, data.size());
pagination.setCurrentPageIndex(pageIndex);
table.setItems(FXCollections.observableArrayList(data.subList(fromIndex, toIndex)));
return mainBorder;
}
}
The first problem is that during the initialization of the controller the createPage method is executed twice. Which seems pretty strange to me. The initialization of the parent class requires only the assignment of the boundle source to a property of the class. The second problem is that at the click of a single page the call to the createPage method goes into a loop generating a stack overflow. As with the guides, the method returns the container in which the table is included. The pagination element is external to it and therefore should not be cyclically reloaded. Some advice?
In any case, the entire application is based on spring boot
EDIT: CHANGE METHOD
I modified this
private Node createPage(int pageIndex) {
log.info("Page index: " + pageIndex);
int fromIndex = pageIndex * paginationSize;
int toIndex = Math.min(fromIndex + paginationSize, data.size());
table.setItems(FXCollections.observableArrayList(data.subList(fromIndex, toIndex)));
return table;
}
and it's initialized and execute once ...but layout explode
Ok instead of
pagination.setPageFactory
just use
pagination.currentPageIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
createPage(newValue.intValue());
}
});
then change createPage method in this way
private void createPage(int pageIndex) {
log.info("Page index: " + pageIndex);
int fromIndex = pageIndex * paginationSize;
int toIndex = Math.min(fromIndex + paginationSize, data.size());
table.setMinHeight(400);
table.getItems().clear();
table.setItems(FXCollections.observableArrayList(data.subList(fromIndex, toIndex)));
}
moreover
private void createPage(int pageIndex) {
int fromIndex = pageIndex * paginationSize;
int toIndex = Math.min(fromIndex + paginationSize, data.size());
table.getItems().clear();
table.setItems(FXCollections.observableArrayList(data.subList(fromIndex, toIndex)));
}
moreover borderpane node is not necessary

JavaFX and Spring - Reusable FXML component

I am building a GUI with Scene Builder in JavaFX and Spring Boot. Several of my scenes have one element in common, which is a simple KPI indicator, called PerformanceBeacon. Depending on the configuration, done by the controller class, the beacon can be used e.g. for project parameters like budget, time and so one. Ideally, even a single scene may have multiple of this elements.
The issue is, having multiple KPI beacons in a single scene does somehow not work, since all embedded controllers are pointing to the same memory and hence, I can only manipulate one - which is the last - KPI beacon.
The snipped from the fxml file looks like this:
...
<VBox layoutX="301.0" layoutY="325.0" spacing="6.0" AnchorPane.leftAnchor="2.0" AnchorPane.topAnchor="24.0">
<children>
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedTimeKPI" />
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedBudgetKPI"/>
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedResourcesKPI"/>
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedScopeKPI"/>
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedEffortKPI"/>
<fx:include source="PerformanceBeacon.fxml" fx:id="embeddedQualityKPI"/>
</children>
</VBox>
...
The embedded controllers are defined in the controller class of the enclosing UI and the code should "only" set the label of each of the Hyperlinks in each PerformanceBeacon.
#Controller
public class ProductManagerCtrl implements Initializable {
...
#FXML protected PerformanceBeaconCtrl embeddedTimeKPIController;
#FXML protected PerformanceBeaconCtrl embeddedBudgetKPIController;
#FXML protected PerformanceBeaconCtrl embeddedResourcesKPIController;
#FXML protected PerformanceBeaconCtrl embeddedScopeKPIController;
#FXML protected PerformanceBeaconCtrl embeddedEffortKPIController;
#FXML protected PerformanceBeaconCtrl embeddedQualityKPIController;
...
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
...
initializePerformanceIndicator();
...
}
protected void initializePerformanceIndicator() {
embeddedBudgetKPIController.setHyperlink("Budget", null);
embeddedScopeKPIController.setHyperlink("Scope", null);
embeddedTimeKPIController.setHyperlink("Time", null);
embeddedQualityKPIController.setHyperlink("Quality", null);
embeddedResourcesKPIController.setHyperlink("Resources", null);
embeddedEffortKPIController.setHyperlink("Effort estimates", null);
}
When I debug the code, each of these embedded controllers points to the same memory. So effectively I have one controller and can only manipulate the last element. In the end the last beacon has the "Effort estimates" label and all others are unchanged.
The PerformanceBeacon.fxml is rather simple. I copy it, in case you want to try yourself
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.Double?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.shape.Polygon?>
<GridPane styleClass="pane" stylesheets="#../stylesheets/PerformanceBeacon.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.agiletunes.productmanager.controllers.PerformanceBeaconCtrl">
<children>
<Circle fx:id="beacon" fill="#1fff4d" radius="10.0" stroke="BLACK" strokeType="INSIDE" GridPane.columnIndex="1" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets bottom="12.0" left="12.0" right="6.0" top="12.0" />
</GridPane.margin>
</Circle>
<Polygon fx:id="trendArrow" fill="#11ff1c" rotate="45.0" stroke="#2e229a" strokeType="INSIDE" GridPane.columnIndex="2" GridPane.valignment="CENTER">
<points>
<Double fx:value="-10.0" />
<Double fx:value="10.0" />
<Double fx:value="-5.0" />
<Double fx:value="10.0" />
<Double fx:value="-5.0" />
<Double fx:value="15.0" />
<Double fx:value="5.0" />
<Double fx:value="15.0" />
<Double fx:value="5.0" />
<Double fx:value="10.0" />
<Double fx:value="10.0" />
<Double fx:value="10.0" />
<Double fx:value="0.0" />
<Double fx:value="-10.0" />
</points>
<GridPane.margin>
<Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</GridPane.margin>
</Polygon>
<Hyperlink fx:id="hyperlink" onAction="#showDetails" text="Subject">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Hyperlink>
</children>
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="170.0" />
<ColumnConstraints minWidth="10.0" />
<ColumnConstraints minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
</GridPane>
And last but not least the Controller of the PerformanceBeacon
#Controller
public class PerformanceBeaconCtrl {
#FXML protected Circle beacon;
#FXML private Polygon trendArrow;
#FXML private Hyperlink hyperlink;
#FXML
public void showDetails(ActionEvent event) {
// TODO
}
public void setHyperlink(String label, URL url) {
Platform.runLater(() -> {
hyperlink.setText(label);
});
}
public void setBeaconColor(Color color) {
Platform.runLater(() -> {
beacon.setFill(color);
});
}
public void setTrend(Double angle) {
Platform.runLater(() -> {
trendArrow.rotateProperty().set(angle);
});
}
}
I am successfully using embedded UI elements / controller also elsewhere in my code. But never the same element multiple times.
I am not sure whether this is a Spring side effect?
What am I doing wrong? Thank you in advance.
The described observation suggests that the issue was related to a singleton somewhere in the design. My suspicion regarding the Spring influence was right. The ProductManagerCtrl controller class gets created using
fxmlLoader.setControllerFactory(c -> ProdMgrApp.springContext.getBean(c));
and as it turns out, this approach creates singletons by default. Therefore all embedded controller are also singletons. The way to fix it is to annotate the PerformanceBeaconCtrl controller class also with
#Scope("prototype")

Populating ComboBox with JAVA FX MVC

I need to populate JAVAFX ComboBox with value using an MVC but somehow i cant get the combobox to work. Please help me,,
LoginController.java
package weltes.finance.controller;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
/**
* Created by engineering09 on 12/18/2015.
*/
public class LoginController {
#FXML private ComboBox userComboBox;
#FXML private void fillComboBox(){
userComboBox.getItems().addAll("Item1", "Item2");
}
}
loginscreen.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="163.0" prefWidth="357.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="weltes.finance.controller.LoginController">
<children>
<ComboBox fx:id="userComboBox" layoutX="15.0" layoutY="14.0" prefHeight="25.0" prefWidth="328.0" promptText="Select Registered User" />
<PasswordField layoutX="15.0" layoutY="56.0" prefHeight="25.0" prefWidth="328.0" promptText="Enter Password" />
<Button layoutX="15.0" layoutY="97.0" mnemonicParsing="false" prefHeight="51.0" prefWidth="328.0" text="Button" />
</children>
</AnchorPane>
First list the items in the FXML.
<ComboBox fx:id="userComboBox" editable="false" layoutX="14.0" layoutY="22.0" onAction="#setTimeMultiplier">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
Then add or remove items from the ObservableArrayList.
userComboBox.getItems().removeAll(comboTime.getItems());
userComboBox.getItems().addAll(...);

How to make a resizable SplitPane within a Tab in JavaFX 2.0 (preferably using SceneBuilder and/or FXML)?

The question basically says it all, with the caveat that I'm a newbie to GUI design in general and JavaFX in particular. To reproduce in SceneBuilder, drag a TabPane onto the scene, and drag a SplitPane into the first tab's AnchorPane. Adjust (drag) the size of the SplitPane to match the the size of the tab. This is not a problem if the SplitPane is not a child (I only tested it as a (grand)child of a Tab so far). You'll have something like this:
Upon preview, if you try to expand the window size by dragging, the SplitPane won't resize:
Here is the FXML I get from SceneBuilder, using the process described at the top of this post with perhaps a few minor changes that didn't help:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="0.0" minWidth="0.0" prefHeight="386.0" prefWidth="613.0">
<children>
<SplitPane dividerPositions="0.29797979797979796" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="358.0" prefWidth="600.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="158.0" prefWidth="277.0" />
</items>
</SplitPane>
</children></AnchorPane>
</content>
</Tab>
<Tab text="Untitled Tab 2">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
The problem is that your SplitPane is a child of an AnchorPane. In case you have something under AnchorPane, it is anchored to specific co-ordinates. To allow the child to take up the whole are, you need to specify the AnchorPane's anchor to zero.
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<tabs>
....
<SplitPane dividerPositions="0.29797979797979796" maxHeight="-Infinity"
maxWidth="-Infinity" prefHeight="358.0" prefWidth="600.0"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
....
</tabs>
</TabPane>
Using scene-builder, you can assign the AnchorPane Constraints by:
Select the child under AnchorPane
Move to Layout section on the right hand side panel
You will see a section called AnchorPane constraints, just type in the four section values and save

Resources