If we have a Stage then Scene includes 2 Panes
the 1st Pane contains Button and the 2nd Pane is empty
could we load other fxml file inside this 2nd Pane?
fxml1: VBox
|_Pane1-->Button
|_Pane2
///////////////
fxml2: Pane--> Welcome to fxml 2
"when we click the button load the fxml2 inside Pane2 of fxml1"
Then after click
====I finally found this works after trying !====Thank you guys
#FXML Pane secPane;
public void loadFxml (ActionEvent event) {
Pane newLoadedPane = FXMLLoader.load(getClass().getResource("/application/fxml2.fxml"));
secPane.getChildren().add(newLoadedPane);
}
I finally found this works after trying !
#FXML Pane secPane;
public void loadFxml (ActionEvent event) {
Pane newLoadedPane = FXMLLoader.load(getClass().getResource("/application/fxml2.fxml"));
secPane.getChildren().add(newLoadedPane);
}
Just replacing the field in your controller class won't change the scene graph.
secPane is just a reference to a node in the scene graph.
If secPane is just a placeholder, you could replace it in the parent's child list:
public void loadFxml (ActionEvent event) {
// load new pane
Pane newPane = FXMLLoader.load(getClass().getResource("/application/Login2.fxml"));
// get children of parent of secPane (the VBox)
List<Node> parentChildren = ((Pane)secPane.getParent()).getChildren();
// replace the child that contained the old secPane
parentChildren.set(parentChildren.indexOf(secPane), newPane);
// store the new pane in the secPane field to allow replacing it the same way later
secPane = newPane;
}
This assumes of course, that getClass().getResource("/application/Login2.fxml") yields the correct resource and does not return null (which happens if no resource with the given name is available)
You can implement something like this :
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Title");
primaryStage.setScene(createScene(loadMainPane("path_of_your_fxml")));
primaryStage.show();
}
private Pane loadMainPane(String path) throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(path));
return mainPane;
}
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(mainPane);
return scene;
}
Then you can create a separate class call Navigation to store all your fxml paths:
public class Navigator {
private final String P1;
private final String P2;
//then you can implement getters...
public String getP1() {
return P1;
}
public String getP2() {
return p2;
}
private static FxmlController Controller;
public static void loadPane(String fxml) {
try {
FxmlController.setPane(
(Node) FXMLLoader.load(Navigator.class.getResource(fxml)));
} catch (IOException e) {
e.printStackTrace();
}
}
public Navigator() throws IOException {
this.P1 = "p1.fxml";
this.P2 = "p2.fxml";}
Then you can load your pane in your button like below:
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
.
Related
Currently I have a main controller which controls adding elements to a Border Pane's left pane for navigation and center pane for content. I am also using fx:root to load new FXML layouts, each with a separate controller into the center pane of the Border Pane. Trying to call the setCenter method of the MainController from a center pane controller my center pane is not updated. I'm guessing the newly created controller is not being associated with the mainPane in the main Controller. How do I associate them?
Main Ctonroller
This loads both the center and left panes with the appropriate views and associated controllers using either button.
public class MainController {
#FXML
BorderPane mainPane;
#FXML
Button orderBtn, adminBtn;
#FXML
static
Label statusTxt;
public void initialize(){
orderBtn.setOnAction(event -> {
setCenter(new ControllerConnector("/view/OrderView.fxml"));
setNav(new ControllerConnector("/view/OrderNav.fxml"));
});
adminBtn.setOnAction(event -> {
setCenter(new ControllerConnector("/view/ProductView.fxml"));
setNav(new ControllerConnector("/view/AdminNav.fxml"));
});
public static void updateStatus(String string) {
statusTxt.setText(string);
}
public void setCenter(ControllerConnector connector){
this.mainPane.setCenter(connector);
}
public void setNav(ControllerConnector connector){
this.mainPane.setLeft(connector);
}
}
Product Controller
The search function works in this controller which is loaded from the associated view so I know the controller was initialized. When the newBtn is clicked nothing happens.
public class ProductController {
#FXML
TableView<Product> productTable;
#FXML
TableColumn prodCol, sizeCol, categoryCol, priceCol;
#FXML
TextField searchTxt;
#FXML
Button searchBtn, newBtn, editBtn, deleteBtn;
public void initialize() throws SQLException {
ObservableList<Product> products = ProductDAO.getProducts();
prodCol.setCellValueFactory(new PropertyValueFactory<Product, String>("name"));
sizeCol.setCellValueFactory(new PropertyValueFactory<Product, String>("size"));
priceCol.setCellValueFactory(new PropertyValueFactory<Product, Double>("price"));
categoryCol.setCellValueFactory(new PropertyValueFactory<Product, String>("category"));
productTable.setItems(products);
searchBtn.setOnAction(event -> {
ObservableList<Product> searchProducts = FXCollections.observableArrayList();
String searchString = searchTxt.getText();
for (Product product : products) {
if(product.getName().contains(searchString) || product.getSize().contains(searchString)){
searchProducts.add(product);
}
}
products.removeAll(products);
products.addAll(searchProducts);
});
searchTxt.setOnMouseClicked(event -> {
products.removeAll(products);
try {
products.addAll(ProductDAO.getProducts());
} catch (SQLException exception) {
exception.printStackTrace();
}
});
newBtn.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/MainView.fxml"));
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
MainController mainController = loader.getController();
mainController.setCenter(new ControllerConnector("/view/NewProductView.fxml"));
});
}
}
ControllerConnector
Using this class to create a Node to pass into the Border Pane set mehtods.
public class ControllerConnector extends GridPane {
public ControllerConnector(String fxmlPath){
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlPath));
fxmlLoader.setRoot(this);
try{
fxmlLoader.load();
}catch (IOException e){
e.printStackTrace();
}
}
}
I'm making an application with JavaFX and Scene Builder.
I have two controllers:Controller and FontController
I have Main class that launch my program and open Stage with first fontroller (Controller)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/card/card.fxml"));
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a label and a button in Controller. When I click on the button a method is called and new window with second controller appears(FontController):
#FXML private Button btnFont;
#FXML private Label category1
#FXML
void changeFont(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("font.fxml"));
Parent rootFont = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Select Font");
stage.setScene(new Scene(rootFont));
stage.show();
} catch (Exception e) {
System.out.println("can't load new window");
}
}
There is the button "OK" and label in FontCOntroller:
#FXML private Label fontLabel;
#FXML private Button btnFontOk;
Please tell me, what should I do to send and apply text from label in FontController when I click on the burtton "OK" to label in Controller?
SOLUTION FOUND:
I created class "Context" in my project directory to make all controllers communicate each other. You can add as many controllers as you want there.
Here it looks like:
package card;
public class Context {
private final static Context instance = new Context();
public static Context getInstance() {
return instance;
}
private Controller controller;
public void setController(Controller controller) {
this.controller=controller;
}
public Controller getController() {
return controller;
}
private FontController fontController;
public void setFontController(FontController fontController) {
this.fontController=fontController;
}
public FontController getFontController() {
return fontController;
}
}
Controller:
I created getters and setters (ALT + Insert in IDEA) for Label that I wanna change
public Label getCategory1() {
return category1;
}
public void setCategory1(Label category1) {
this.category1 = category1;
}
To get FontController variables and methods through Context class I placed line of code
//getting FontController through Context Class
FontController fontCont = Context.getInstance().getFontController();
I registered Controller in Context class through my initialize method (my class implements Initializable)
#FXML
public void initialize(URL location, ResourceBundle resources) {
//register Controller in Context Class
Context.getInstance().setController(this);
}
FontController:
to get Controller variables and methods I placed this code:
//getting Controller variables and methods through Context class
Controller cont = Context.getInstance().getController();
I also registered FontController in Context class through initialize method:
#Override
public void initialize(URL location, ResourceBundle resources) {
//register FontController in Context Class
Context.getInstance().setFontController(this);
}
Method that send text and text color from label in this FontController to label in Controller when I click on button:
#FXML
void applyFont(ActionEvent event) {
cont.getCategory1().setText(fontLabel.getText());
cont.getCategory1().setTextFill(fontLabel.getTextFill());
}
*By creating Context class you can make controllers communicate each other and create as many controllers as you want there. Controllers see variables and methods of each other
I am creating an application where I'm combining FXML and regular javaFX to create an application. I'm, however, experiencing problems writing EventHandlers for a Stage-subclass called AddItemWindow that generates custom windows.
The application shows a list of items (keys and weapons) in an inventory. The user can add items, and needs to press a button to add the item of his choice (Add Key or Add Weapon).
A new window then pops up, where the user has to input the relevant data. It will generate a GridPane with the TextFields where the user can input the data. This will be a custom GridPane, depending on the ItemType. It will then load the FXML and add the GridPane.
With the below code, I am getting LoadExceptions for my SetOnAction-code for the buttons cancelling the window or confirming the new item.
Would any-one know where I'm making an error?
/* StartUp Class*/
package main;
//imports from javafx and java
import domain.DomainController;
import gui.OverviewWindow;
public class StartUpGUI extends Application {
#Override
public void start(Stage primaryStage) {
Parent root = new OverviewWindow(new DomainController());
Scene scene = new Scene(root);
primaryStage.setTitle("Weapons and Keys");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String... args) {
Application.launch(StartUpGUI.class, args);
}
}
/* OverviewWindow, subclass of BorderPane */
package gui;
//imports from javafx and java
import domain.DomainController;
import domain.ItemType;
public class OverviewWindow extends BorderPane{
#FXML
private Button btnAddWeapon;
#FXML
private Button btnAddKey;
#FXML
private TextArea txaOverview;
private DomainController dc;
this.dc = dc;
FXMLLoader loader = new FXMLLoader(getClass().getResource("OverviewWindow.fxml"));
loader.setRoot(this);
loader.setController(this);
try{
loader.load();
txaOverview.setText(dc.showOverview()); // showOverview returns a String containing a toString of all items in the inventory
}
catch (IOException ex){
throw new RuntimeException(ex);
}
}
#FXML
public void btnAddWeaponOnAction(ActionEvent event){
try{
add(ItemType.WEAPON); // ItemType is an Enum where all the properties of the items are defined; for Weapon: name, weight, level, power, used(boolean)
}
catch (Exception e){
throw e;
}
}
#FXML
public void btnAddKeyOnAction(ActionEvent event){
try{
add(ItemType.SLEUTEL); // ItemType is an Enum where all the properties of the items are defined; for Key: name, weight, level, door
}
catch (Exception e){
throw e;
}
}
private void add(ItemType itemType){
Stage stage = new Stage();
stage.setTitle(itemType== VoorwerpSoort.WEAPON ? "Add Weapon" : "Add Key");
AddItem addItem = new AddItem(dc,itemType,this);
addItem.setOnHiding(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent e){
txaOverview.setText(dc.showOverview()); // when we close the AddItemWindow, we will update the item overview by having the domain controller get this data from the repository
}
});
addItem.show();
}
}
/* AddItemWindow, a subclass of Stage*/
package gui;
// import relevant javafx and java classes
import domain.DomainController;
import domain.ItemType;
public class AddItemWindow extends Stage {
#FXML
private BorderPane addRoot;
#FXML
private Button btnOK;
#FXML
private Button btnCancel;
private DomainController dc;
private ItemType itemType;
private Parent parent;
private TextField[] txfAttributes;
public AddItemWindow(DomainController dc, ItemType itemType, OverviewWindow overviewWindow){
this.dc = dc;
this.itemType = itemType;
this.parent = overviewWindow;
this.setScene(buildGUI(dc,itemType,overviewWindow));
}
private Scene buildGUI(DomeinController dc, VoorwerpSoort vwps, OverzichtSchermController ovsController){
Parent root = new BorderPane();
GridPane properties = new GridPane();
properties.setPadding(new Insets(10));
properties.setHgap(10);
properties.setVgap(10);
ColumnConstraints col1 = new ColumnConstraints();
col1.setHalignment(HPos.RIGHT);
ColumnConstraints col2 = new ColumnConstraints();
properties.getColumnConstraints().addAll(col1, col2);
String[] attributes = itemType.attributeNames();
txfAttributes = new TextField[attributes.length];
for(int i = 0; i<attributes.length; i++){
properties.add(new Label(attributes[i]),0,i);
properties.add(txfAttributes[i] = new TextField(),1,i);
}
((BorderPane) root).setCenter(properties);
FXMLLoader loader = new FXMLLoader(getClass().getResource("AddItemWindow.fxml"));
loader.setRoot(root);
loader.setController(root);
try{
loader.load();
return new Scene(root);
}
catch(IOException e){
throw new RuntimeException(e);
}
}
// NOT WORKING
#FXML
public void btnOKOnAction(){
addItem();
}
// NOT WORKING
#FXML
public void btnCancelOnAction(ActionEvent event){
hide();
}
private void voorwerpToevoegen(){ // we're calling the domaincontroller to add the new item to the repository
switch (itemType)
{
// for the item, add an item by getting the value of each TextField, which are the
// parameters for a constructor of the new item
case WEAPON:
dc.addWeapon(txfAttributes[0].getText(),
Double.parseDouble(txfAttributes[1].getText()),
Integer.parseInt(txfAttributes[2].getText()),
Integer.parseInt(txfAttributes[3].getText()),
Boolean.parseBoolean(txfAttributes[4].getText()));
break;
case KEY:
dc.addKey(txfAttributes[0].getText(),
Double.parseDouble(txfAttributes[1].getText()),
Integer.parseInt(txfAttributes[2].getText()),
Integer.parseInt(txfAttributes[3].getText()));
break;
}
hide();
}
}
At the top you have #FXML private Button btnOK; which is good.
To specify the button's action in Java code, you can use this Java 8 syntax in your buildGUI() method (see JavaFX 8 Event Handling Examples):
btnOK.setOnAction((event) -> addItem());
If you're not using Java 8, see UI Control Sample.
You don't need the methods annotated with #FXML.
I have a reset and Draw button on a slide panel. I choose a desired file from file chooser, which is in RootLayout class, and pass the file path to a controller class. Then it does some processes and initializes field in DataCunstructor class. By clicking on Draw a TreeTableView will be shown on slide pane, which is in MainController class. When I click my reset button the table will be cleared but I do not know how to reset the chosen path. After reseting if I click Draw again the same treetable comes up. and If I choose another file and hit Draw, the program breaks.
How can I reset all fields including the path to null, and be able to choose another file and process that one?
Here is my Draw and Reset in MainController class:
public void treeTableDraw(ActionEvent event) {
drawTable();//creates the TreeTableView
numberOfFunctions= dc.getFuncAll().size();
numberOfOrganizations = dc.getSortedAssignedOrg().size();
funcLabel.setText(numberOfFunctions+"");//set Lable value
orgLabel.setText(numberOfOrganizations + "");//set Lable value
}
public void treeTableReset(ActionEvent event){
funcLabel.setText("0");//reset Label
orgLabel.setText("0");
treeTable.getColumns().clear(); //clears columns (TreeTable)
///////////////////////////////////////
//non of the following did the path reset//
///////////////////////////////////////
//dc = new DataConstructor();
//Controller controller = new Controller();
//controller.setPath(null);
RootLayoutController rlc = loader.getController();
rlc.reset();
}
My File Chooser in RootLayout class:
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
"3lgm2 files (*.z3lgm)", "*z3lgm");
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
path = file.toString();
if (path != null) {
new Controller(path);
}
}
public void reset(){
path = null;
}
I add OverView at the center of rootlayout here at main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
//private ObservableList<DataConstructor> treeTableData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("IT-Saturation");
initRootLayout();
showOverView();
}
private void showOverView() {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/OverView.fxml"));
AnchorPane overView = (AnchorPane) loader.load();
rootLayout.setCenter(overView);
}catch(IOException e){
e.printStackTrace();
}
}
private void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
//show scene containing the root layout
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(
getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
//gives controller access to main app
RootLayoutController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
File file = getFilePath();
if (file != null) {
loadDataFromFile(file);
}
}
/**
* Returns the main stage.
* #return primaryStage
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
Just declare a reset() in your RootLayout class:
public class RootLayout {
private Path path;
#FXML
private void handleOpen() {
...
path = file.toString();
}
public void reset() {
path= null;
}
}
Never construct the constructor using the keyword new, always get it from the FXMLLoader.
public class MainController {
...
RootLayoutController controller = loader.getController();
controller.reset();
...
}
Is there any way of getting the Stage/Window object of an FXML loaded file from the associated class controller?
Particularly, I have a controller for a modal window and I need the Stage to close it.
I could not find an elegant solution to the problem. But I found these two alternatives:
Getting the window reference from a Node in the Scene
#FXML private Button closeButton ;
public void handleCloseButton() {
Scene scene = closeButton.getScene();
if (scene != null) {
Window window = scene.getWindow();
if (window != null) {
window.hide();
}
}
}
Passing the Window as an argument to the controller when the FXML is loaded.
String resource = "/modalWindow.fxml";
URL location = getClass().getResource(resource);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = (Parent) fxmlLoader.load();
controller = (FormController) fxmlLoader.getController();
dialogStage = new Stage();
controller.setStage(dialogStage);
...
And FormController must implement the setStage method.
#FXML
private Button closeBtn;
Stage currentStage = (Stage)closeBtn.getScene().getWindow();
currentStage.close();
Another way is define a static getter for the Stage and Access it
Main Class
public class Main extends Application {
private static Stage primaryStage; // **Declare static Stage**
private void setPrimaryStage(Stage stage) {
Main.primaryStage = stage;
}
static public Stage getPrimaryStage() {
return Main.primaryStage;
}
#Override
public void start(Stage primaryStage) throws Exception{
setPrimaryStage(primaryStage); // **Set the Stage**
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
}
Now you Can access this stage by calling
Main.getPrimaryStage()
In Controller Class
public class Controller {
public void onMouseClickAction(ActionEvent e) {
Stage s = Main.getPrimaryStage();
s.close();
}
}