2016-08-17 37 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