Search code examples
javabuttonjavafxfxml

JavaFX - transform from disable to enable button


Controller of the father class

this class contains 3 button 'btnSocieta', 'btnUnitaLocali', 'btnReparti'. The first is enable from default other 2 are disable. In the stackPane i show the start page where you can find right away.

they have to change state after calling method setEnableBtnUnitaLocale or the other one

package View.Controllers;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import com.jfoenix.controls.JFXButton;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Creazione implements Initializable {

    @FXML
    private JFXButton btnSocieta;

    @FXML
    private JFXButton btnUnitaLocali;

    @FXML
    private JFXButton btnReparti;

    @FXML
    private StackPane stackPane;

    @Override
    public void initialize(URL url, ResourceBundle rb) {

        btnUnitaLocali.setDisable(true);
        btnReparti.setDisable(true);

        try {
            Parent root = FXMLLoader.load(getClass().getResource("/View/fxml/creazione_societa.fxml"));
            stackPane.getChildren().removeAll();
            stackPane.getChildren().setAll(root);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void switchToSocieta(javafx.event.ActionEvent event) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("/View/fxml/creazione_societa.fxml"));
        stackPane.getChildren().removeAll();
        stackPane.getChildren().setAll(root);
    }

    public void switchToUnitaLocali(javafx.event.ActionEvent event) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("/View/fxml/creazione_unitalocale.fxml"));
        stackPane.getChildren().removeAll();
        stackPane.getChildren().setAll(root);
    }

    public void switchToReparti(javafx.event.ActionEvent event) {
        System.out.println("You clicked me!");
    }

    public void setEnableBtnUnitaLocale() {
        
        btnUnitaLocali.setDisable(false);
        System.out.println("setEnableBtnUnitaLocale()");

    }

    public void setEnableBtnReparti() {

        btnReparti.setDisable(false);
    }

}

FXML file menaged by Creazione that have a StackPane that contains CreazioneSocieta


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

<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>

<BorderPane xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="View.Controllers.Creazione">
   <top>
      <Pane prefHeight="48.0" prefWidth="600.0" BorderPane.alignment="CENTER">
         <children>
            <Label alignment="CENTER" layoutX="181.0" layoutY="16.0" minWidth="34.0" prefHeight="17.0" text="&gt;&gt;" />
            <Label alignment="CENTER" layoutX="382.0" layoutY="16.0" text="&gt;&gt;" />
            <JFXButton fx:id="btnSocieta" alignment="CENTER" layoutX="53.0" layoutY="12.0" onAction="#switchToSocieta" text="Società / Ente" />
            <JFXButton fx:id="btnUnitaLocali" alignment="CENTER" layoutX="255.0" layoutY="12.0" onAction="#switchToUnitaLocali" text="Unità locali" />
            <JFXButton fx:id="btnReparti" alignment="CENTER" layoutX="453.0" layoutY="12.0" onAction="#switchToReparti" text="Reparti" />
         </children>
      </Pane>
   </top>
   <center>
      <StackPane fx:id="stackPane" prefHeight="352.0" prefWidth="600.0" BorderPane.alignment="CENTER" />
   </center>
</BorderPane>

Controller of the FXML file

this controller only scope is to call the method 'setEnableBtnUnitaLocale' that we've see before through method 'salvaSocieta'

package View.Controllers;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import com.jfoenix.controls.JFXButton;

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


public class CreazioneSocieta implements Initializable {

    @FXML
    JFXButton btnSalva;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        

    }

    public void salvaSocieta(javafx.event.ActionEvent event) throws IOException{
        
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/fxml/main_creazione.fxml"));
        Parent root = (Parent) loader.load();
        
        Creazione controller = loader.getController();
        controller.setEnableBtnUnitaLocale();

    }
    
}

FXML File that contains the save button

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

<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Pane?>

<Pane xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1"
   fx:controller="View.Controllers.CreazioneSocieta">
   <children>
      <Label layoutX="51.0" layoutY="139.0" prefHeight="17.0" prefWidth="93.0"
         text="Ragione Sociale:" />
      <TextField layoutX="153.0" layoutY="135.0" prefHeight="25.0" prefWidth="437.0" />
      <Label layoutX="51.0" layoutY="203.0" prefHeight="17.0" prefWidth="56.0" text="Indirizzo:" />
      <TextField layoutX="115.0" layoutY="199.0" prefHeight="25.0" prefWidth="330.0" />
      <Label layoutX="51.0" layoutY="324.0" prefHeight="17.0" prefWidth="56.0" text="Telefono:" />
      <TextField accessibleText="INTEGER" layoutX="115.0" layoutY="320.0" prefHeight="25.0"
         prefWidth="330.0" />
      <Label layoutX="51.0" layoutY="262.0" prefHeight="17.0" prefWidth="43.0" text="e-mail:" />
      <TextField layoutX="115.0" layoutY="258.0" prefHeight="25.0" prefWidth="330.0" />
      <Label layoutX="467.0" layoutY="203.0" prefHeight="17.0" prefWidth="56.0" text="Provincia:" />
      <TextField accessibleText="INTEGER" layoutX="533.0" layoutY="199.0" prefHeight="25.0"
         prefWidth="56.0" />
      <JFXButton onAction="#salvaSocieta" contentDisplay="CENTER" defaultButton="true"
         graphicTextGap="0.0" layoutX="573.0" layoutY="421.0" prefHeight="45.0" prefWidth="0.0"
         ripplerFill="#bfbdbd" textFill="WHITE" textOverrun="CLIP">
         <graphic>
            <ImageView fitHeight="37.0" fitWidth="37.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@../icons/save_icon.png" />
               </image>
            </ImageView>
         </graphic>
      </JFXButton>
   </children>
</Pane>

Problem: The 'btnUnitaLocale' doesn't became enable after calling the function 'setEnableBtnUnitaLocale'


Solution

  • Use a MVC approach. Define a Model class that contains the state of the application, with mechanisms to observe the values and respond to them changes. You can use JavaFX Properties to do this easily (see this or the "Bindings and Observable Values" section of this, and links from there)

    public class Model {
    
        private final BooleanProperty societySaved = new SimpleBooleanProperty(false);
    
        public BooleanProperty societySavedProperty() {
            return societySaved ;
        }
    
        public final boolean isSocietySaved() {
            return societySavedProperty().get();
        }
    
        public final void setSocietySaved(boolean societySaved) {
            societySavedProperty().set(societySaved);
        }
    
    
        // similarly for other properties as needed
    }
    

    In your Creazione controller class, define a field for the model and bind the state of the button to the state in the model:

    public class Creazione {
    
        @FXML
        private JFXButton btnSocieta;
    
        @FXML
        private JFXButton btnUnitaLocali;
    
        @FXML
        private JFXButton btnReparti;
    
        @FXML
        private StackPane stackPane;
    
        private Model model ;
    
        public void setModel(Model model) {
            this.model = model ;
            btnUnitaLocali.disableProperty().bind(model.societySavedProperty().not());
            // similarly for other buttons and properties in the model
        }
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
    
            //btnUnitaLocali.setDisable(true);
            //btnReparti.setDisable(true);
    
            try {
                Parent root = FXMLLoader.load(getClass().getResource("/View/fxml/creazione_societa.fxml"));
                stackPane.getChildren().removeAll();
                stackPane.getChildren().setAll(root);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        // ... (further modifications to methods shown later)
    
    
    
        // these are not needed
        // public void setEnableBtnUnitaLocale() {
            
        //     btnUnitaLocali.setDisable(false);
        //     System.out.println("setEnableBtnUnitaLocale()");
    
        // }
    
        // public void setEnableBtnReparti() {
    
        //     btnReparti.setDisable(false);
        // }
    
    }
    

    Similarly, in your other controllers, define a reference to the model and update the property. For example:

    public class CreazioneSocieta  {
    
        private Model model ;
    
        public void setModel(Model model) {
            this.model = model;
        }
    
    
        public void salvaSocieta(ActionEvent event) {
            
            model.setSocietySaved(true);
    
        }
        
    }
    

    Now, in your application code, you need to create a Model instance and when you load the initial view, pass the model to the controller:

    Model model = new Model();
    FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/fxml/main_creazione.fxml"));
    Parent root = (Parent) loader.load();
            
    Creazione controller = loader.getController();
    controller.setModel(model);
    
    Scene scene = new Scene(root);
    stage.setScene(scene);
    // etc.
    

    Finally, back in your Creazione class, be sure to pass the same model instance to the other controllers. For example:

    public class Creazione {
    
        // existing code ...
    
        public void switchToSocieta(javafx.event.ActionEvent event) throws IOException {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/fxml/creazione_societa.fxml"));
            Parent root = loader.load();
            CreazioneSocieta controller = loader.getController();
            controller.setModel(model);
    
            stackPane.getChildren().removeAll();
            stackPane.getChildren().setAll(root);
        }
    }
    

    For better decoupling of the controllers, in particular in the context of switching scene, study this example.