2017-06-01 3 views
1

Je développe une application utilisant JavaFX dans laquelle je crée TextFields dynamique à l'intérieur d'un GridPane et il y a un bouton qui est désactivé par défaut comme ceci:JavaFx: Comment comparer les valeurs de TextFields créées dynamiquement à l'intérieur de GridPane?

enter image description here

donc ce que je veux est si la colonne 1 les valeurs de TextField sont inférieures à la colonne 3 valeurs TextField, bouton doit être activé pour comme ceci:

enter image description here

Mais que dire si l'un de la colonne 3 valeur TextField devient inférieure à la colonne 1 valeur TextField de même ligne, il devrait diSAb bouton le et montrer que la frontière TextField spécifique de couleur rouge et lorsque la souris en vol stationnaire au-dessus de ce champ devrait afficher un avertissement:

enter image description here

Je crée TextField comme ceci:

public static GridPane table(int rows){ 
      GridPane table = new GridPane(); 

      for(int i=0; i<rows; i++){ 
      TextField textField1 = new JFXTextField(); 
      textField1.setAlignment(Pos.CENTER); 
      TextField textField2 = new JFXTextField(); 
      textField2.setAlignment(Pos.CENTER); 
      TextField textField3 = new JFXTextField(); 
      textField3.setAlignment(Pos.CENTER); 

      //add them to the GridPane 
      table.add(textField1, 0, i+1); 
      table.add(textField2, 1, i+1); 
      table.add(textField3, 2, i+1); 
     } 
     return table; 
    } 

Après que je « m créer une autre méthode pour renvoyer le composant de la table à la ligne spécifique et de la colonne comme suit:

public static Node getComponent (int row, int column, GridPane table) { 
     for (Node component : table.getChildren()) { // loop through every node in the table 
      if(GridPane.getRowIndex(component) == row && 
          GridPane.getColumnIndex(component) == column) { 
       return component; 
      } 
     } 

     return null; 
    } 

I essayé de faire de ce type b ut il ne fonctionne pas (ici je convertir des valeurs en chaîne et en comparant juste pour le chèque):

private boolean isTextEqual(GridPane table, Button button){ 

     for(Node node : table.getChildren()){ 
      if(node instanceof TextField){ 
       for(int i=1 ; i<=ComboBox().getValue(); i++){ 
        String str = ((TextField)DynamicGridpanes.getComponent (i, 0, table)).getText(); 
        ((TextField)DynamicGridpanes.getComponent (i, 2, table)).textProperty().addListener((obs, old, newV)->{ 
        if(newV.toString()==str){ 
         button.setDisable(false); 
        } 
        else{ 
         button.setDisable(true); 
        } 
       }); 
        } 
       } 
      } 

     return true; 
    } 
+0

Avez-vous toujours le '' DatePicker' et checkbox' dans votre table? – Yahya

+0

Oui @Yahya j'ai. Je l'ai posté comme pour simplifier la question – Junaid

+0

Donc, fondamentalement, vous avez trois colonnes de 'TextFields' (suivis les uns des autres) et les' CheckBoxs' puis les 'DatePickers'? Et chaque colonne 'TextField' est-elle différente dans' GridPane'. Combien de 'GridPane' avez-vous? – Yahya

Répondre

1

En fait, ce n'est pas facile à faire ce que vous voulez, parce que le code que vous avez besoin d'être refactorisé (le code ne vise pas à faire de telles exigences avancées, mais il est très bien pour les exigences de base que vous avez).Cependant, vous pouvez faire quelque chose comme ceci:

D'abord, définir une TextField variable globale être mis à jour avec la last row index du malade (De là, vous conclurai que cela va changer la couleur de bordure pour ONE invalide TextField à un moment):

public static int textFieldIndex = -1; 

maintenant, avec l'aide de la méthode que vous avez déjà getComponent (int row, int column, GridPane table), créer une autre méthode statique pour vérifier si ALLTextFields ont des valeurs valides à un moment donné:

/** 
* This method to check at run time with every change in any TextField 
* if the corresponding TextField has a valid value(i.e contains number and 
* the first TextField value is less than the second) 
* @param table 
* @param numRows 
*/ 
private static boolean hasValidValue(GridPane table, int numRows){ 
    // cycle through every row in the table 
    // and compare every two TextFields 
    for(int i=0; i<numRows; i++){ 
     try{ // try because user may enters a non-number input (to avoid crash) 
     // the first TextField is always at column index 0 , the second at column index 3 
     if(Integer.parseInt(((TextField)(getComponent (i, 0, table))).getText())> 
      Integer.parseInt(((TextField)(getComponent (i, 3, table))).getText())){ 
      // before returning false 
      textFieldIndex = i; // update at which row the TextField is less 
      return false; 
      } 
     }catch(NumberFormatException e){ // if it contains invalid input(non-digit) 
      return false; 
     } 
    } 
    return true; 
} 

Maintenant, vous devez utiliser la méthode ci-dessus dans la méthode validateTable() et faire quelques ajustements:

// pass the comboBox.getValue() to the third parameter 
private void validateTable(GridPane table, Button button, int numRows) { 

    for(Node textField : table.getChildren()){ 
     if(textField instanceof TextField){ 
     ((TextField)textField).textProperty().addListener((obs, old, newV)->{ 
      // first of all remove the red border from the invalid TextField (if any) 
      // we know that via textFieldIndex which should be -1 if there is no lesser 
      // actually it's a pain 
      if(textFieldIndex!=-1){ 
      ((TextField) getComponent(textFieldIndex, 3, table)).setStyle(""); 
      } 
      if(isAllFilled(table)){ // if all filled (you already have this method) 
      if(hasValidValue(table,numRows)){ // check for validity 
       button.setDisable(false); // then make the button active again 
      } 
      else{// if it's not a valid value 
        // re-style the TextField which has lesser value 
       ((TextField) getComponent(textFieldIndex, 3, table)). 
             setStyle("-fx-border-color: red;"); 
        button.setDisable(true); 
      } 
      } 
      else{ 
       button.setDisable(true); 
      } 
     }); 
    } 
    } 
} 

Maintenant dans votre tabPane ChangeListener ajouter la troisième para à la méthode (parce que vous l'avez déjà vous avez juste besoin d'ajouter la valeur de ComboBox:

tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){ 
    .... 
    .... 
    .... 
    // I think you have here anchorPane not containerB in the original code 
    validateTable((GridPane) containerB.getChildren().get(0), test, comboBox.getValue()); 
} 

test

Test

+0

@Junaid Tout va bien? – Yahya

+0

Idk @Yahya mais "hasValidValue" retourne toujours vrai – Junaid

+0

Oui @Yahya ça fonctionne maintenant mais il montre une bordure rouge à la fois comme s'il y a plus de valeurs dans la deuxième colonne moins puis dans la première colonne il n'y a pas de bordure rouge , alors pouvons-nous réaliser quelque chose comme ça? Ce n'est pas un gros problème, mais si c'est implémentable ça ira mieux. – Junaid

1

Vous pouvez créer les liaisons qui font la validation lorsque vous créez les champs de texte. Cela évite d'avoir à parcourir les nœuds enfants du volet de la grille, ce qui ne semble pas très robuste.

Déclarer un tableau de liaisons booléennes (il y aura un pour chaque ligne):

private BooleanBinding[] rowValidationBindings ; 

Ensuite, vous pouvez faire

public static GridPane table(int rows){ 
    GridPane table = new GridPane(); 

    rowValidationBindings = new BooleanBinding[rows]; 

    for(int i=0; i<rows; i++){ 
     TextField textField1 = new JFXTextField(); 
     textField1.setAlignment(Pos.CENTER); 
     TextField textField2 = new JFXTextField(); 
     textField2.setAlignment(Pos.CENTER); 
     TextField textField3 = new JFXTextField(); 
     textField3.setAlignment(Pos.CENTER); 

     rowValidationBindings[i] = Bindings.createBooleanBinding(
      () -> { 
       if (textField1.getText().matches("\\d+") && 
        textField3.getText().matches("\\d+")) { 
        int value1 = Integer.parseInt(textField1.getText()); 
        int value3 = Integer.parseInt(textFIeld3.getText()); 
        return value3 > value1 ; 
       } else { 
        return false ; 
       } 
      }, textField1.textProperty(), textField2.textProperty() 
     ); 

     //add them to the GridPane 
     table.add(textField1, 0, i+1); 
     table.add(textField2, 1, i+1); 
     table.add(textField3, 2, i+1); 
    } 

    button.disableProperty().bind(Bindings.createBooleanBinding(
     () -> ! Stream.of(rowValidationBindings).allMatch(BooleanBinding::get), 
     rowValidationBindings 
    )); 

    return table; 
} 

Vous pouvez également ajouter le style au champ de texte directement dans la boucle for:

textField3.styleProperty().bind(Bindings 
    .when(rowValidationBindings[i]) 
    .then("") 
    .otherwise("-fx-border-color: red")); // or whatever you are using for style 

et pour tooltips:

Tooltip tooltip = new Tooltip(); 
tooltip.textProperty().bind(Bindings.concat("Value must be greater than ",textField1.textProperty())); 
textField3.tooltipProperty().bind(Bindings 
    .when(rowValidationBindings[i]) 
    .then((Tooltip)null) 
    .otherwise(tooltip)); 
+0

Pour être honnête, je referais énormément ce code, par exemple. créer une classe qui représente chaque ligne, avec des méthodes pour ajouter à un volet de la grille et récupérer les valeurs. Ensuite, exposez un 'ObservableBooleanValue valide' de cette classe (avec des méthodes pour extraire les valeurs). Ensuite, vous pouvez garder une liste d'instances de cette classe, et il serait facile de créer la liaison pour le bouton. –

+0

Pouvez-vous s'il vous plaît élaborer un peu plus que voulez-vous dire par ce "créer une classe qui représentait chaque ligne" – Junaid

+0

@Junaid bien que vous n'avez pas à faire cela, il serait juste de simplifier votre code. Je voulais juste créer une classe avec trois champs de texte, un champ pour "valide" et les méthodes dont vous avez besoin (par exemple 'void addToGridPaneRow (int row)', 'String getText1()', etc). Mais la solution affichée devrait fonctionner. . –