2016-08-17 63 views
0

我在FXML中構建DialogPane,我試圖弄清楚如何響應按鈕按下對話框,因爲onAction不是ButtonType的有效參數。我附加了我的FXML和控制器類。關於DialogPane的文檔很少,更不用說在FXML中做這件事,所以我不知道如何繼續。FXML設置按鈕類型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"); 
    } 

} 

回答

2

通常你不需要這樣做。您通常會在Dialog<ButtonType>中顯示一個DialogPane,調用它的showAndWait()方法,該方法返回代表按下按鈕的Optional<ButtonType>(如果有的話)。所以正常使用會像

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(); 
    } 

} 

並進行以下更改FXML文件:

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

那麼你會使用這項功能:

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... 
    }); 

作爲替代曝光來自文本字段的文本,您可以在控制器本身中定義一個processLogin()方法,該方法可以讀取文本字段並執行任何您需要執行的操作:

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... 
    } 
} 

然後就去做

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

如果你真的需要註冊一個onAction處理程序的登錄按鈕,這樣做在控制器的initialize()方法:

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 -> { /* ... */ }); 
} 

但這真正違反了對話框API的預期用途。


最後一個替代方案將是重寫createButton方法DialogPane。要做到這一點,你需要一個DialogPane的子類,這意味着使用FXML custom component pattern

因此,這看起來是這樣的:

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... 
    } 
} 

,然後FXML會是什麼樣

<?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> 

如果你正在尋找你會使用這個版本與

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

所以儘可能地將其封裝到登錄窗格和fxml中,這可能是最乾淨的選項。

請注意,DialogPane的用法在DialogDialogPane的API文檔中有相當完整的記錄。

+0

作爲最後一個提示,我更新了我的FXML以將DialogPane包裝在對話框中,然後我可以使用FXMLLoader從FMXL和showAndWait使用您的示例創建對話框的實例。最難的部分是,我發現的所有例子都沒有使用FXML作爲他們的基礎,我試圖儘可能多地做到這一點。 – Shawn