2016-08-17 1 views
0

Ich baue ein DialogPane in FXML und ich versuche herauszufinden, wie man auf Knopfdruck auf dem Dialog reagiert, da onAction kein gültiger Parameter für ButtonType ist. Ich habe meine FXML- und Controller-Klasse angehängt. Es gibt sehr wenig Dokumentation über DialogPane und noch weniger über das Ausführen in FXML, so dass ich mir nicht sicher bin, wie ich vorgehen soll.FXML Set ButtonType onAction

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

<?import javafx.scene.control.ButtonType?> 
<?import javafx.scene.control.DialogPane?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.control.TextField?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 

<DialogPane fx:id="loginPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="LoginController"> 
    <content> 
     <GridPane hgap="5.0" vgap="5.0"> 
      <columnConstraints> 
       <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> 
       <ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.0" /> 
      </columnConstraints> 
      <rowConstraints> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
      </rowConstraints> 
      <children> 
       <Label text="Driver Name" /> 
       <TextField fx:id="driverTxt" GridPane.columnIndex="1" /> 
       <Label text="URL" GridPane.rowIndex="1" /> 
       <TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" /> 
       <Label text="Username" GridPane.rowIndex="2" /> 
       <TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" /> 
       <Label text="Password" GridPane.rowIndex="3" /> 
       <TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" /> 
      </children> 
     </GridPane> 
    </content> 
    <buttonTypes> 
     <ButtonType fx:id="loginButton" text="Login" /> 
     <ButtonType fx:id="cancelBtn" text="Cancel" /> 
    </buttonTypes> 
</DialogPane> 

import javafx.fxml.FXML; 
import javafx.scene.Scene; 
import javafx.scene.control.ButtonType; 
import javafx.scene.control.DialogPane; 
import javafx.scene.control.TextField; 

public class LoginController { 

    @FXML 
    DialogPane loginPane; 
    @FXML 
    TextField driverTxt; 
    @FXML 
    TextField urlTxt; 
    @FXML 
    TextField userTxt; 
    @FXML 
    TextField passTxt; 
    @FXML 
    ButtonType loginButton; 

    @FXML 
    private void loginButtonAction(){ 
     // How do I do something here 
    } 

    public void initialize() { 
     driverTxt.setText("org.postgresql.Driver"); 
     urlTxt.setText("jdbc:postgresql://localhost/postgres"); 
     userTxt.setText("postgres"); 
     passTxt.setText("postgres"); 
    } 

} 

Antwort

2

Normalerweise müssten Sie dies nicht tun. Sie würden normalerweise eine DialogPane in einer Dialog<ButtonType> aufrufen, die ihre showAndWait() Methode aufruft, die eine Optional<ButtonType> zurückgibt, die die gedrückte Taste (wenn vorhanden) darstellt. So würde die normale Gebrauch so etwas wie

public class LoginController { 

    public static final ButtonType LOGIN = new ButtonType("Login"); 

    @FXML 
    DialogPane loginPane; 
    @FXML 
    TextField driverTxt; 
    @FXML 
    TextField urlTxt; 
    @FXML 
    TextField userTxt; 
    @FXML 
    TextField passTxt; 


    public void initialize() { 
     driverTxt.setText("org.postgresql.Driver"); 
     urlTxt.setText("jdbc:postgresql://localhost/postgres"); 
     userTxt.setText("postgres"); 
     passTxt.setText("postgres"); 
    } 

    public String getDriver() { 
     return driverTxt.getText(); 
    } 

    public String getUrl() { 
     return urlTxt.getText(); 
    } 

    public String getUser() { 
     return userTxt.getText(); 
    } 

    public String getPass() { 
     return pass.getText(); 
    } 

} 

sein und die folgenden Änderungen an Ihre FXML Datei machen:

<buttonTypes> 
    <LoginController fx:constant="LOGIN" /> 
    <ButtonType fx:constant="CANCEL" /> 
</buttonTypes> 

Dann würden Sie dies mit verwenden:

Dialog<ButtonType> dialog = new Dialog<>(); 
FXMLLoader dialogLoader = new FXMLLoader(getClass().getResource("Login.fxml")); 
dialog.setDialogPane(dialogLoader.load()); 
LoginController controller = dialogLoader.getController(); 
dialog.showAndWait().filter(LoginController.LOGIN::equals) 
    .ifPresent(button -> { 
     String driver = controller.getDriver(); 
     // etc etc 
     // process login... 
    }); 

Als Alternative zum Belichten der Text aus den Textfeldern, können Sie eine processLogin() Methode in der Steuerung selbst definieren, die die Textfelder liest und alles getan hat, was Sie tun müssen:

public class LoginController { 

    public static final ButtonType LOGIN = new ButtonType("Login"); 

    @FXML 
    DialogPane loginPane; 
    @FXML 
    TextField driverTxt; 
    @FXML 
    TextField urlTxt; 
    @FXML 
    TextField userTxt; 
    @FXML 
    TextField passTxt; 


    public void initialize() { 
     driverTxt.setText("org.postgresql.Driver"); 
     urlTxt.setText("jdbc:postgresql://localhost/postgres"); 
     userTxt.setText("postgres"); 
     passTxt.setText("postgres"); 
    } 

    public void processLogin() { 
     String driver = driverTxt.getText(); 
     // etc... 
     // process login... 
    } 
} 

dann tun nur

// ... 
dialog.showAndWait().filter(LoginController.LOGIN::equals) 
    .ifPresent(button -> controller.processLogin()); 

Wenn Sie wirklich einen onAction Handler mit der Login-Button, tun Sie es in der initialize() Methode in der Steuerung müssen registrieren:

public void initialize() { 
    driverTxt.setText("org.postgresql.Driver"); 
    urlTxt.setText("jdbc:postgresql://localhost/postgres"); 
    userTxt.setText("postgres"); 
    passTxt.setText("postgres"); 

    Button login = (Button) loginPane.lookupButton(loginButton); 
    login.setOnAction(e -> { /* ... */ }); 
} 

aber das ist wirklich gegen die beabsichtigte Verwendung der Dialogbereich-API.


Eine letzte Alternative wäre, die createButton Methode in DialogPane außer Kraft zu setzen. Dazu benötigen Sie eine Unterklasse von DialogPane, die die Verwendung der FXML custom component pattern bedeuten würde.

So würde dies in etwa so aussehen:

public class LoginPane extends DialogPane { 

    public static final ButtonType LOGIN = new ButtonType("Login"); 

    @FXML 
    TextField driverTxt; 
    @FXML 
    TextField urlTxt; 
    @FXML 
    TextField userTxt; 
    @FXML 
    TextField passTxt; 

    public LoginPane() { 
     try { 
      FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginPane.fxml")); 
      loader.setRoot(this); 
      loader.setController(this); 
      loader.load(); 
     } catch (IOException exc) { 
      // bad if you get here... 
      throw new UncheckedIOException(exc); 
     } 
    } 

    @Override 
    public Node createButton(ButtonType buttonType) { 
     Node button = super.createButton(buttonType); 
     if (buttonType == LOGIN) { 
      ((Button) button).setOnAction(e -> processLogin()); 
     } 
     return button ; 
    } 


    public void initialize() { 
     driverTxt.setText("org.postgresql.Driver"); 
     urlTxt.setText("jdbc:postgresql://localhost/postgres"); 
     userTxt.setText("postgres"); 
     passTxt.setText("postgres"); 
    } 

    public void processLogin() { 
     String driver = driverTxt.getText(); 
     // etc... 
     // process login... 
    } 
} 

und die FXML dann wie

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

<?import javafx.scene.control.ButtonType?> 
<?import javafx.scene.control.DialogPane?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.control.TextField?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 

<fx:root type="DialogPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1"> 
    <content> 
     <GridPane hgap="5.0" vgap="5.0"> 
      <columnConstraints> 
       <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> 
       <ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.0" /> 
      </columnConstraints> 
      <rowConstraints> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
       <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
      </rowConstraints> 
      <children> 
       <Label text="Driver Name" /> 
       <TextField fx:id="driverTxt" GridPane.columnIndex="1" /> 
       <Label text="URL" GridPane.rowIndex="1" /> 
       <TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" /> 
       <Label text="Username" GridPane.rowIndex="2" /> 
       <TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" /> 
       <Label text="Password" GridPane.rowIndex="3" /> 
       <TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" /> 
      </children> 
     </GridPane> 
    </content> 
    <buttonTypes> 
     <LoginPane fx:constant="LOGIN" /> 
     <ButtonType fx:constant="CANCEL" /> 
    </buttonTypes> 
</fx:root> 
aussehen würde

Sie würden diese Version verwenden, mit

Dialog dialog = new Dialog(); 
dialog.setDialogPane(new LoginPane()); 
dialog.showAndWait(); 

also, wenn Sie schauen, Um so viel wie möglich in den Login-Bereich und fxml einzukapseln, ist dies wahrscheinlich die sauberste Option.

Beachten Sie, dass die Verwendung von DialogPane in den API-Dokumenten für Dialog und DialogPane ziemlich vollständig dokumentiert ist.

+0

Als letzte Anmerkung habe ich meine FXML aktualisiert, um DialogPane in einem Dialog zu umbrechen und dann konnte ich einfach den FXMLLoader verwenden, um eine Instanz des Dialogs aus dem FMXL und showAndWait zu erstellen. Der schwierigste Teil war, dass all die Beispiele, die ich fand, FXML nicht als Basis benutzten und ich versuche, dort so viel wie möglich zu machen. – Shawn

Verwandte Themen